/**********************************************************************************/
/* wpanel - wpanel.c                                                              */
/*                                                                                */
/* Programmed by Kevin Pickell                                                    */
/*                                                                                */
/* Started December 2009                                                          */
/*                                                                                */
/* http://code.google.com/p/wpanel/	                                              */
/*                                                                                */
/*    wwwterm is free software; you can redistribute it and/or modify             */
/*    it under the terms of the GNU General Public License as published by        */
/*    the Free Software Foundation; version 2.                                    */
/*                                                                                */
/*    wpanel is distributed in the hope that it will be useful,                   */
/*    but WITHOUT ANY WARRANTY; without even the implied warranty of              */
/*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               */
/*    GNU General Public License for more details.                                */
/*                                                                                */
/*    http://www.gnu.org/licenses/gpl.txt                                         */
/*                                                                                */
/*    You should have received a copy of the GNU General Public License           */
/*    along with wpanel; if not, write to the Free Software                       */
/*    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA  */
/*                                                                                */
/**********************************************************************************/

/* this program is a cgi-bin program that runs via a web-browser, you need to     */
/* copy it to the cgi-bin folder on your website and then access it via the       */
/* appropriate url, typically http://example.com/cgi-bin/wpanel                  */

/* this program needs to be compiled with root permissions since it needs to access */
/* the password information to authenticate the user. */

/* how it works: */
/* for most operations it runs, interogates and displays the various system files. */
/* for shell operations it automatically starts a daemon, the daemon then starts a */
/* shell instance. The cgi-bin program communicates with the daemon via a chunk */
/* of shared memory and the daemon communicates with the shell via re-directed */
/* stdin and stdout. When leaving the shell page the daemon is automatically */
/* terminated (except when using Opera, see browser problems below). */

/* browser problems and issues encountered so far */

/* IE8: doesn't support <pre> formatting when changing conentents with Javascript, the pre-mode gets reset */
/* IE8: inline data for images (data:image/gif;base64,...) cannot be very big and gets truncated */
/* FF,IE8,Safari bold monospace font is WIDER than non bold font! This works ok in Chrome */
/* onunload in Chrome won't fire an ajax request, has to change to onbeforeunload */
/* onunload and onbeforeunload events not working at all in Opera */
/* All: ajax return data cannot be binary as it gets mangled so we convert it to base64 format */

/* things that still need to be done / figured out */

/* cannot figure out how to do double wide or double tall fonts on any browser, is it possible? */
/* need to figure out how to do alternate fonts Go,G1 */
/* would like to have drag and drop functionality in the file browse tab to upload/download files */

//wish / todo list:
//make edit-save and drag-drop save send along file len and crc, then after saving check len,crc for validity!
//make list showing files update after rename/delete and upload (use ajax?)
//rename folder
//delete folder
//edit file permissions, user / group
//edit binary files?? or complain that file is not text and therefore not editable!
//drag file or folder from webpage to machines desktop or fileexplorer
//use webfonts for handling custom Terminal fonts G0,G1 etc., also 2x1 or 1x2 stretched
//add set/use tabs

//drag and drop documentation!
//https://developer.mozilla.org/en/Using_files_from_web_applications

extern "C" {

#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <errno.h>
#include <signal.h>

#include <sys/file.h>
#include <sys/dir.h>
#include <sys/stat.h>

#define DIRCHAR "/"
#include <sys/dir.h>
#include <sys/stat.h>
#include <sys/file.h>
 #include <dirent.h>

#include <linux/types.h>
#include <linux/unistd.h>
#include <mntent.h>
#include <sys/vfs.h>
#include <pty.h>

//use shared memory to communicate between the daemon and the cgi-bin
#define USEKEY 0xb19b00b5
#include <sys/ipc.h>
#include <sys/shm.h>

#include <netdb.h>
#include <netinet/in.h>
#include <ifaddrs.h>
#include <arpa/inet.h>
#include <sys/sysinfo.h>
#include <sys/utsname.h>
#include <unistd.h>
#include <stdarg.h>

#include <crypt.h>
#include <shadow.h>
#include <pwd.h>
}	//end of extern c
#include <list>
#include <string>
using namespace std;

//show test tab
#define DOTEST 0

//name of program!
#define NAME "wpanel"
#define VERSION "v0.93"
#define DOTTYTEST 1
#define USELOGIN 1
#define SAVELOGS 1
//the Javascript updates can get really slow if NUMLINES is any larger than aprox 300 or so.
#define NUMLINES 300
//NUMLINEBITS is the number of bits required to hold the NUMLINES define above! 
#define NUMLINEBITS 9
#define NUMSHOWLINES 25
//NUMSHOWLINESEM = NUMSHOWLINES *1.2 plus extra (1 extra em) to compensate for the bottom scrollbar height
#define NUMSHOWLINESEM 31
#define NUMCOLS 132

//whenver this time wraps the whole set of line data is re-sent
#define MAXTIME 65535
#define MAXTIMEBITS 16

#define VALSTRINGIFY(s) STRINGIFY(s)
#define STRINGIFY(s) #s

//these are bits
#define STYLE_BOLD 1
#define STYLE_UNDERLINE 2
#define STYLE_BLINK 4
#define STYLE_REVERSE 8
#define STYLE_DOUBLETOP 16
#define STYLE_DOUBLEBOTTOM 32
#define STYLE_CURSOR 64
//this is the number of the bits used above
#define STYLE_BITSUSED 7

/* we have 8 background and 8 forground colors */
#define STYLE_COLORMASK 7
#define STYLE_COLORBITS 3

//using defines instead of enums so we can use VALSTRINGIFY on them!
#define FUNC_UNDEFINED 0
#define FUNC_LOGIN 1
#define FUNC_LOGOUT 2
#define FUNC_AJAX_SHUTDOWN 3
#define FUNC_AJAX_SAVE 4
#define FUNC_BROWSE 5
#define FUNC_VIEW 6
#define FUNC_GET 7
#define FUNC_GETICON 8
#define FUNC_AJAX_RENAME 9
#define FUNC_AJAX_DELETE 10

//sanity check for defines above
#if NUMLINES>=(1<<NUMLINEBITS)
#error 'not enough bits to hold largest value' NUMLINES
#endif

#if MAXTIME>(1<<MAXTIMEBITS)
#error 'not enough bits to hold largest time' MAXTIME
#endif

#if (STYLE_COLORMASK+1)!=(1<<STYLE_COLORBITS)
#error 'color mask and color bits do not agree!'
#endif

static char novalue[]={""};

class ParmEntry
{
public:
	char *m_name;
	char *m_value;	/* if no value then it points to a null */
};

class Parms
{
public:
	Parms() {m_len=0;m_rawparms=0;m_decparms=0;m_path=0;m_numparms=0;m_parmentries=0;m_contentsize=0;m_contentbuffer=0;}
	~Parms() {Purge();}
	void Load(void);
	void Purge(void);
	unsigned int GetNumParms(void) {return m_numparms;}
	const char *FindParmz(const char *parmname);
	ParmEntry *GetParm(unsigned int n);
	void GetParmPathSub(unsigned int n,char *dest);
	const char *GetParmPath(void);
	unsigned int GetParmPathNumSubs(void);
	unsigned int GetContentSize(void) {return m_contentsize;}
	const char *GetContent(void) {return m_contentbuffer;}
private:
	int m_len;
	char *m_rawparms;
	char *m_decparms;
	const char *m_path;
	unsigned int m_numparms;
	ParmEntry *m_parmentries;
	unsigned int m_contentsize;
	char *m_contentbuffer;
};

class Cookies
{
public:
	Cookies();
	~Cookies();
	void Load(void);
	const char *FindCookiez(const char *cookiename);
	void SetCookie(const char *name,const char *value,bool stay);
	void DeleteCookie(const char *name);
private:
	int m_len;
	char *m_parms;
};

const char *Parms::GetParmPath(void)
{
	return(m_path);
}

unsigned int Parms::GetParmPathNumSubs(void)
{
	const char *cp;
	unsigned int n;

	n=0;
	cp=m_path;
	if(cp)
	{
		++cp;	/* skip opening '/' */
		while(cp[0])
		{
			if(cp[0]=='/')
				++n;
			if(cp[0]=='?')
				break;
			++cp;
		}
	}
	return(n);
}

/* copy parm number n to dest string */
void Parms::GetParmPathSub(unsigned int n,char *dest)
{
	const char *cp;

	cp=m_path;
	if(cp)
	{
		++cp;				/* skip opening '/' */
		while(cp[0])
		{
			if(cp[0]=='?')
				break;
			if(cp[0]=='/')
			{
				if(n==0)
					break;
				--n;
			}
			else if(n==0)
				*(dest++)=*(cp);
			++cp;
		}
	}
	*(dest)=0;
	return;
}

void Parms::Load(void)
{
	unsigned int i;
	int j;
	unsigned int data_size;
	char *ev;
	char *inputbuffer=0;
	char *cp=0;
	int numparms;
	unsigned int rr;
	int rb;

	data_size=0;
	m_contentsize=0;
	m_rawparms=0;
	m_decparms=0;
	m_len=0;
	m_numparms=0;
	m_parmentries=0;
	m_path=getenv("PATH_INFO");

	ev=getenv("QUERY_STRING");
	if(ev)
	{
		data_size=(int)strlen(ev);
		if(data_size)
		{
			inputbuffer=new char[data_size+1];
			if(!inputbuffer)
				return;

			for(i=0;i<data_size;i++)
				inputbuffer[i]=ev[i];
			inputbuffer[data_size]=0;
		}
	}

	ev=getenv("CONTENT_LENGTH");
	if(ev)
	{
		m_contentsize=atoi(ev);
		
		if(m_contentsize)
		{
			m_contentbuffer=new char[m_contentsize+1];
			if(!m_contentbuffer)
				return;

			rr=0;

			do
			{
				rb=(int)fread(m_contentbuffer+rr,1,m_contentsize-rr,stdin);
				if(!rb)
				{
					if(!rr)
						return;
				}
				rr+=rb;
			}while(rr<m_contentsize);
			m_contentbuffer[m_contentsize]=0;
		}
	}

	if(!data_size && m_contentsize)
	{
		data_size=m_contentsize;
		inputbuffer=m_contentbuffer;
		m_contentsize=0;
		m_contentbuffer=0;
	}

	if(!data_size)
		return;	/* noparms to scan */

	m_rawparms=inputbuffer;
	m_decparms=cp=new char[data_size+1];
	if(!m_decparms)
		return;
	
	/* count the number of parms */
	numparms=1;
	for(i=0;i<data_size;)
	{
		if(inputbuffer[i++]=='&')
			++numparms;
	}

	m_parmentries=new ParmEntry[numparms];

	j=0;
	m_parmentries[0].m_name=cp;
	m_parmentries[0].m_value=novalue;
	m_numparms=1;
	for(i=0;i<data_size;)
	{
		char c;

		c=inputbuffer[i++];
		if(c=='%') 
		{
			char digit,c1,c2;
			c1=inputbuffer[i++];
			c2=inputbuffer[i++];
			digit=(c1>='A')?(((c1&0xdf)-'A')+10):(c1-'0');
			digit<<=4;
			digit+=(c2>='A')?(((c2&0xdf)-'A')+10):(c2-'0');
			cp[j++]=digit;		/* re-construcrted digit */
		}
		else if((c=='&'))
		{
			cp[j++]=0;							/* parm seperator */
			m_parmentries[m_numparms].m_name=cp+j;
			m_parmentries[m_numparms].m_value=novalue;
			++(m_numparms);
		}
		else if((c=='='))
		{
			cp[j++]=0;		/* word/value seperator */
			m_parmentries[m_numparms-1].m_value=cp+j;
		}
		else if(c=='+')
			cp[j++]=' ';
		else
			cp[j++]=c;
	}
	cp[j]=0;
	m_len=j;

	/* scan parms looking for any parm starting with 'amp;' */
	/* this is because some old browsers don't decode them properly */

	for(i=0;i<m_numparms;++i)
	{
		if(!strncmp(m_parmentries[i].m_name,"amp;",4))
			m_parmentries[i].m_name+=4;
	}

	return;	/* ok */
}

void Parms::Purge(void)
{
	if(m_contentbuffer)
	{
		delete []m_contentbuffer;
		m_contentbuffer=0;
	}
	if(m_parmentries)
	{
		delete []m_parmentries;
		m_parmentries=0;
	}
	if(m_rawparms)
	{
		delete []m_rawparms;
		m_rawparms=0;
	}
	if(m_decparms)
	{
		delete []m_decparms;
		m_decparms=0;
	}
}

ParmEntry *Parms::GetParm(unsigned int n)
{
	return(m_parmentries+n);
}

/* returns a pointer to the parms value if found! */
const char *Parms::FindParmz(const char *parmname)
{
	unsigned int i;
	ParmEntry *pe;

	pe=m_parmentries;
	for(i=0;i<m_numparms;++i)
	{
		if(!strcmp(pe->m_name,parmname))
			return(pe->m_value);
		++pe;
	}
	return(0);
}

Cookies::Cookies()
{
	m_len=0;
	m_parms=0;
}

Cookies::~Cookies()
{
	if(m_parms)
	{
		delete []m_parms;
		m_parms=0;
	}
}

void Cookies::Load(void)
{
	int i,j,data_size;
	char *ev;
	char *inputbuffer=0;
	char *cp=0;

	data_size=0;
	m_len=0;
	m_parms=0;
	ev=getenv("HTTP_COOKIE");
	if(ev)
	{
		data_size=(int)strlen(ev);
		if(data_size)
		{
			inputbuffer=new char [data_size+1];
			for(i=0;i<data_size;i++)
				inputbuffer[i]=ev[i];
			inputbuffer[data_size]=0;
		}
	}

	if(data_size)
	{
		m_parms=cp=new char[data_size+1];
		j=0;
		for(i=0;i<data_size;)
		{
			char c;

			c=inputbuffer[i++];
			if(c=='%') 
			{
				char digit,c1,c2;
				c1=inputbuffer[i++];
				c2=inputbuffer[i++];
				digit=(c1>='A')?(((c1&0xdf)-'A')+10):(c1-'0');
				digit<<=4;
				digit+=(c2>='A')?(((c2&0xdf)-'A')+10):(c2-'0');
				cp[j++]=digit;		/* re-construcrted digit */
			}
			else if((c=='=') || (c==';'))
			{
				cp[j++]=0;		/* word/value seperator */
			}
			else if(c=='+')
				cp[j++]=' ';
			else
				cp[j++]=c;
		}
		cp[j]=0;
		m_len=j;
	}
	if(inputbuffer)
		delete []inputbuffer;
	return;	/* ok */
}

/* returns a pointer to the cookie value if found! */
const char *Cookies::FindCookiez(const char *cookiename)
{
	int i,wl;
	char *cp;
	
	cp=m_parms;
	for(i=0;i<m_len;)
	{
		wl=(int)strlen(cp+i)+1;
		while(cp[i]==' ')
		{
			i++;
			wl--;
		}
		if(!strcmp(cp+i,cookiename))
		{
			cp=cp+i+wl;
			if(!cp[0])
				return(0);	/* null value, return zero */
			return(cp);
		}
	
		i+=wl;			/* skip cookie name */
		wl=(int)strlen(cp+i)+1;	/* get length of cookie value */
		i+=wl;
	}
	return(0);
}

void Cookies::DeleteCookie(const char *name)
{
	SetCookie(name,0,true);
}

void Cookies::SetCookie(const char *name,const char *value,bool stay)
{
	const char *cp;
	char c;

	printf("Set-Cookie: %s=",name);

	if(value)
	{
		cp=value;
		while(*(cp))
		{
			c=*(cp);
			if(c==' ')
				printf("+");
			else if( ((c>='0') && (c<='9')) ||
						((c>='A') && (c<='Z')) ||
						((c>='a') && (c<='z')) )
			{
				printf("%c",c);
			}
			else
			{
				char c1,c2;
				
				c1=(c>>4)&15;
				c2=c&15;
				if(c1<10)
					c1+='0';
				else
					c1+='A'-10;
				if(c2<10)
					c2+='0';
				else
					c2+='A'-10;
				printf("%%%c%c",c1,c2);
			}
			cp++;
		}
	}

	if(stay)
		printf("; expires=\"Sun, 31-Dec-2030 23:59:59 GMT\"");
	printf("; path=/\012");
}

void dlog(const char *fmt,...)
{
#if SAVELOGS
	char estring[8192];
	va_list args;
	FILE *f;

	va_start(args, fmt);
	vsnprintf(estring, sizeof(estring), fmt, args);
	va_end(args);

	f=fopen(NAME ".log","a+");
	if(f)
	{
		fwrite(estring,1,strlen(estring),f);
		fclose(f);
	}
#endif
}

void dlog2(const char *fmt,...)
{
#if SAVELOGS
	char estring[8192];
	va_list args;
	FILE *f;

	va_start(args, fmt);
	vsnprintf(estring, sizeof(estring), fmt, args);
	va_end(args);

	f=fopen(NAME "raw.log","a+");
	if(f)
	{
		fwrite(estring,1,strlen(estring),f);
		fclose(f);
	}
#endif
}

static void sleepms(int ms)
{
	struct timeval delay;

	delay.tv_sec=ms/1000000L;	
	delay.tv_usec=ms%1000000L;	
	select(0,0,0,0,&delay);
}

static const char *defhomedir={"/"};

static const char *ev[]={"GATEWAY_INTERFACE",
		"SERVER_NAME",
		"SERVER_SOFTWARE",
		"SERVER_PROTOCOL",
		"SERVER_PORT",
		"REQUEST_METHOD",
		"PATH_INFO",
		"PATH_TRANSLATED",
		"SCRIPT_NAME",
		"DOCUMENT_ROOT",
		"QUERY_STRING",
		"HTTP_HOST",
		"REMOTE_HOST",
		"REMOTE_ADDR",
		"AUTH_TYPE",
		"REMOTE_USER",
		"REMOTE_IDENT",
		"CONTENT_TYPE",
		"CONTENT_LENGTH",
		"HTTP_COOKIE",
		"HTTP_FORM",
		"HTTP_ACCEPT",
		"HTTP_USER_AGENT",
		"HTTP_IF_MODIFIED_SINCE",
		"HTTP_REFERER"};

char *myasctime(const struct tm *timeptr)
{
    static const char *wday_name[7] = {
        "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
    };
    static const char *mon_name[12] = {
        "Jan", "Feb", "Mar", "Apr", "May", "Jun",
        "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
    };
    static char result[64];

    sprintf(result, "%.3s,%3d %.3s %d %.2d:%.2d:%.2d GMT",
        wday_name[timeptr->tm_wday],
        timeptr->tm_mday, 
        mon_name[timeptr->tm_mon],
		1900 + timeptr->tm_year,
		timeptr->tm_hour,
        timeptr->tm_min,
		timeptr->tm_sec);
    return result;
}

static void df(char *device, const char *mountPoint)
{
	struct statfs s;
	long blocks_used;
	long blocks_percent_used;

	if (statfs(mountPoint, &s) != 0)
	{
		perror(mountPoint);
		return;
	}

	if (s.f_blocks > 0)
	{
		blocks_used = s.f_blocks - s.f_bfree;
		blocks_percent_used = (long)
			(blocks_used * 100.0 / (blocks_used + s.f_bavail) + 0.5);

		printf("<tr><td>%s&nbsp;</td><td>%9ld&nbsp;</td><td>%9ld&nbsp;</td><td>%9ld&nbsp;</td><td>%3ld%%&nbsp;</td><td>%s&nbsp;</td></tr>",
			   device,
			   (long) (s.f_blocks * (s.f_bsize / 1024.0)),
			   (long) ((s.f_blocks - s.f_bfree) * (s.f_bsize / 1024.0)),
			   (long) (s.f_bavail * (s.f_bsize / 1024.0)),
			   blocks_percent_used, mountPoint);

	}
}

static const unsigned char b64[]={"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"};

class EncodeBase64
{
public:
	EncodeBase64() {m_bits=0;m_numbits=0;}
	void Add(unsigned char c);
	void Flush(void);
private:
	unsigned int m_bits;
	unsigned int m_numbits;
};

void EncodeBase64::Add(unsigned char c)
{
	if(!m_numbits)
		m_bits=c;
	else
		m_bits=(m_bits<<8)|(c&255);
	m_numbits+=8;

	while(m_numbits>=6)
	{
		printf("%c",b64[(m_bits>>(m_numbits-6))&63]);
		m_numbits-=6;
	}
}

void EncodeBase64::Flush(void)
{
	if(m_numbits)
	{
		m_bits<<=8;
		m_numbits+=8;
		printf("%c",b64[(m_bits>>(m_numbits-6))&63]);
	}
}

static const char dirc[]={DIRCHAR};

class ReadEntry
{
public:
	const char *m_fullname;
	const char *m_shortname;
};

class ReadDir
{
public:
	ReadDir();
	~ReadDir() {Close();}
	bool Open(const char *path);
	ReadEntry *GetEntry(void);
	void Close(void);
	ReadEntry *GetFullName(const char *shortname);
private:
	DIR *m_dp;
	struct stat statbuf;
	char *m_nameplace;
	char *m_cp;
	ReadEntry m_re;
	char m_tempname[8192];
};

ReadDir::ReadDir()
{
	m_dp=0;
}

void ReadDir::Close(void)
{
	if(m_dp)
	{
		closedir(m_dp);
		m_dp=0;
	}
}

bool ReadDir::Open(const char *path)
{
	//close previous (if any)
	Close();

	strcpy(m_tempname,path);
	m_dp=opendir (m_tempname);
	if(!m_dp)
		return(false);
	// if path doesn't end in '/' then append '/'
	if(m_tempname[strlen(m_tempname)-1]!=dirc[0])
		strcat(m_tempname,DIRCHAR);
	m_nameplace=m_tempname+strlen(m_tempname);

	m_re.m_fullname=m_tempname;
	m_re.m_shortname=m_nameplace;

	return(true);
}

//return entries until done, then return null
ReadEntry *ReadDir::GetEntry(void)
{
    struct dirent *ep;

	ep=readdir(m_dp);
	if(!ep)
		return(false);

	strcpy(m_nameplace,ep->d_name);	/* put name after path/ */

	return(&m_re);
}

ReadEntry *ReadDir::GetFullName(const char *shortname)
{
	strcpy(m_nameplace,shortname);	/* put name after path/ */
	return(&m_re);
}

const char mtab_file[] = "/etc/mtab";

enum
{
#if USELOGIN
TAB_LOGIN,
TAB_LOGOUT,
#endif
TAB_SYSTEM,
TAB_FILES,
TAB_SHELL,
TAB_USERS,
TAB_MOUNTS,
TAB_CPU,
TAB_DEVICES,
TAB_FILESYSTEMS,
TAB_INTERFACES,
TAB_PORTS,
TAB_PROCESSES,
TAB_WEB,
#if DOTEST
TAB_TEST,
#endif
TAB_CREDITS,
TAB_NUMTABS};

const char g_javascript_editfile[]={"\
var crc32x=[0x00000000,0x77073096,0xEE0E612C,0x990951BA,0x076DC419,0x706AF48F,0xE963A535,0x9E6495A3,0x0EDB8832,0x79DCB8A4,0xE0D5E91E,0x97D2D988,0x09B64C2B,0x7EB17CBD,0xE7B82D07,0x90BF1D91,0x1DB71064,0x6AB020F2,0xF3B97148,0x84BE41DE,0x1ADAD47D,0x6DDDE4EB,0xF4D4B551,0x83D385C7,0x136C9856,0x646BA8C0,0xFD62F97A,0x8A65C9EC,0x14015C4F,0x63066CD9,0xFA0F3D63,0x8D080DF5,0x3B6E20C8,0x4C69105E,0xD56041E4,0xA2677172,0x3C03E4D1,0x4B04D447,0xD20D85FD,0xA50AB56B,0x35B5A8FA,0x42B2986C,0xDBBBC9D6,0xACBCF940,0x32D86CE3,0x45DF5C75,0xDCD60DCF,0xABD13D59,0x26D930AC,0x51DE003A,0xC8D75180,0xBFD06116,0x21B4F4B5,0x56B3C423,0xCFBA9599,0xB8BDA50F,0x2802B89E,0x5F058808,0xC60CD9B2,0xB10BE924,0x2F6F7C87,0x58684C11,0xC1611DAB,0xB6662D3D,0x76DC4190,0x01DB7106,0x98D220BC,0xEFD5102A,0x71B18589,0x06B6B51F,0x9FBFE4A5,0xE8B8D433,0x7807C9A2,0x0F00F934,0x9609A88E,0xE10E9818,0x7F6A0DBB,0x086D3D2D,0x91646C97,0xE6635C01,0x6B6B51F4,0x1C6C6162,0x856530D8,0xF262004E,0x6C0695ED,0x1B01A57B,0x8208F4C1,0xF50FC457,0x65B0D9C6,0x12B7E950,0x8BBEB8EA,0xFCB9887C,0x62DD1DDF,0x15DA2D49,0x8CD37CF3,0xFBD44C65,0x4DB26158,0x3AB551CE,0xA3BC0074,0xD4BB30E2,0x4ADFA541,0x3DD895D7,0xA4D1C46D,0xD3D6F4FB,0x4369E96A,0x346ED9FC,0xAD678846,0xDA60B8D0,0x44042D73,0x33031DE5,0xAA0A4C5F,0xDD0D7CC9,0x5005713C,0x270241AA,0xBE0B1010,0xC90C2086,0x5768B525,0x206F85B3,0xB966D409,0xCE61E49F,0x5EDEF90E,0x29D9C998,0xB0D09822,0xC7D7A8B4,0x59B33D17,0x2EB40D81,0xB7BD5C3B,0xC0BA6CAD,0xEDB88320,0x9ABFB3B6,0x03B6E20C,0x74B1D29A,0xEAD54739,0x9DD277AF,0x04DB2615,0x73DC1683,0xE3630B12,0x94643B84,0x0D6D6A3E,0x7A6A5AA8,0xE40ECF0B,0x9309FF9D,0x0A00AE27,0x7D079EB1,0xF00F9344,0x8708A3D2,0x1E01F268,0x6906C2FE,0xF762575D,0x806567CB,0x196C3671,0x6E6B06E7,0xFED41B76,0x89D32BE0,0x10DA7A5A,0x67DD4ACC,0xF9B9DF6F,0x8EBEEFF9,0x17B7BE43,0x60B08ED5,0xD6D6A3E8,0xA1D1937E,0x38D8C2C4,0x4FDFF252,0xD1BB67F1,0xA6BC5767,0x3FB506DD,0x48B2364B,0xD80D2BDA,0xAF0A1B4C,0x36034AF6,0x41047A60,0xDF60EFC3,0xA867DF55,0x316E8EEF,0x4669BE79,0xCB61B38C,0xBC66831A,0x256FD2A0,0x5268E236,0xCC0C7795,0xBB0B4703,0x220216B9,0x5505262F,0xC5BA3BBE,0xB2BD0B28,0x2BB45A92,0x5CB36A04,0xC2D7FFA7,0xB5D0CF31,0x2CD99E8B,0x5BDEAE1D,0x9B64C2B0,0xEC63F226,0x756AA39C,0x026D930A,0x9C0906A9,0xEB0E363F,0x72076785,0x05005713,0x95BF4A82,0xE2B87A14,0x7BB12BAE,0x0CB61B38,0x92D28E9B,0xE5D5BE0D,0x7CDCEFB7,0x0BDBDF21,0x86D3D2D4,0xF1D4E242,0x68DDB3F8,0x1FDA836E,0x81BE16CD,0xF6B9265B,0x6FB077E1,0x18B74777,0x88085AE6,0xFF0F6A70,0x66063BCA,0x11010B5C,0x8F659EFF,0xF862AE69,0x616BFFD3,0x166CCF45,0xA00AE278,0xD70DD2EE,0x4E048354,0x3903B3C2,0xA7672661,0xD06016F7,0x4969474D,0x3E6E77DB,0xAED16A4A,0xD9D65ADC,0x40DF0B66,0x37D83BF0,0xA9BCAE53,0xDEBB9EC5,0x47B2CF7F,0x30B5FFE9,0xBDBDF21C,0xCABAC28A,0x53B39330,0x24B4A3A6,0xBAD03605,0xCDD70693,0x54DE5729,0x23D967BF,0xB3667A2E,0xC4614AB8,0x5D681B02,0x2A6F2B94,0xB40BBE37,0xC30C8EA1,0x5A05DF1B,0x2D02EF8D];\
function crc32(str)\
{\
var i;\
var sl=str.length;\
var crc=0;\
var ti;\
for(i=0;i<sl;++i)\
{\
ti=(crc^str.charCodeAt(i))&255;\
crc=(crc>>>8)^crc32x[ti];\
}\
return crc;\
}\
var xmlhttp;\
function doEdit()\
{\
document.getElementById('editfile').removeAttribute('disabled');\
document.getElementById('savebutton').removeAttribute('disabled');\
document.getElementById('savebutton').setAttribute('class','ebutton');\
}\
function doSave(savefilename)\
{\
var x=window.confirm('Are you sure you want to save?');\
if (x)\
{\
var contents=document.getElementById('editfile').value;\
xmlhttp=GetXmlHttpObject();\
if (xmlhttp==null)\
return false;\
document.getElementById('status').setAttribute('class','busy');\
document.getElementById('results').innerHTML='';\
xmlhttp.onreadystatechange=stateChanged;\
var url='" NAME "?func=" VALSTRINGIFY(FUNC_AJAX_SAVE) "&amp;fn='+savefilename+'&amp;len='+contents.length+'&amp;crc='+crc32(contents);\
xmlhttp.open('POST',url,true);\
xmlhttp.send(contents);\
return false;\
}\
}\
function stateChanged()\
{\
if (xmlhttp.readyState==4)\
document.getElementById('status').setAttribute('class', 'done');\
document.getElementById('results').innerHTML=xmlhttp.responseText;\
}\
function GetXmlHttpObject()\
{\
if (window.XMLHttpRequest)\
  return new XMLHttpRequest();\
if (window.ActiveXObject)\
  return new ActiveXObject(\"Microsoft.XMLHTTP\");\
return null;\
}"};

const char g_javascript_drop[]={"\n\
var numdrop=0;\
var dropreader=[];\
var dropname=[];\
var dropstate=[];\
var dropresults=[];\
var uploaddir;\
var droparea;\
var droplist;\
var xmlhttp;\
var uploadslot=-1;\
var crc32x=[0x00000000,0x77073096,0xEE0E612C,0x990951BA,0x076DC419,0x706AF48F,0xE963A535,0x9E6495A3,0x0EDB8832,0x79DCB8A4,0xE0D5E91E,0x97D2D988,0x09B64C2B,0x7EB17CBD,0xE7B82D07,0x90BF1D91,0x1DB71064,0x6AB020F2,0xF3B97148,0x84BE41DE,0x1ADAD47D,0x6DDDE4EB,0xF4D4B551,0x83D385C7,0x136C9856,0x646BA8C0,0xFD62F97A,0x8A65C9EC,0x14015C4F,0x63066CD9,0xFA0F3D63,0x8D080DF5,0x3B6E20C8,0x4C69105E,0xD56041E4,0xA2677172,0x3C03E4D1,0x4B04D447,0xD20D85FD,0xA50AB56B,0x35B5A8FA,0x42B2986C,0xDBBBC9D6,0xACBCF940,0x32D86CE3,0x45DF5C75,0xDCD60DCF,0xABD13D59,0x26D930AC,0x51DE003A,0xC8D75180,0xBFD06116,0x21B4F4B5,0x56B3C423,0xCFBA9599,0xB8BDA50F,0x2802B89E,0x5F058808,0xC60CD9B2,0xB10BE924,0x2F6F7C87,0x58684C11,0xC1611DAB,0xB6662D3D,0x76DC4190,0x01DB7106,0x98D220BC,0xEFD5102A,0x71B18589,0x06B6B51F,0x9FBFE4A5,0xE8B8D433,0x7807C9A2,0x0F00F934,0x9609A88E,0xE10E9818,0x7F6A0DBB,0x086D3D2D,0x91646C97,0xE6635C01,0x6B6B51F4,0x1C6C6162,0x856530D8,0xF262004E,0x6C0695ED,0x1B01A57B,0x8208F4C1,0xF50FC457,0x65B0D9C6,0x12B7E950,0x8BBEB8EA,0xFCB9887C,0x62DD1DDF,0x15DA2D49,0x8CD37CF3,0xFBD44C65,0x4DB26158,0x3AB551CE,0xA3BC0074,0xD4BB30E2,0x4ADFA541,0x3DD895D7,0xA4D1C46D,0xD3D6F4FB,0x4369E96A,0x346ED9FC,0xAD678846,0xDA60B8D0,0x44042D73,0x33031DE5,0xAA0A4C5F,0xDD0D7CC9,0x5005713C,0x270241AA,0xBE0B1010,0xC90C2086,0x5768B525,0x206F85B3,0xB966D409,0xCE61E49F,0x5EDEF90E,0x29D9C998,0xB0D09822,0xC7D7A8B4,0x59B33D17,0x2EB40D81,0xB7BD5C3B,0xC0BA6CAD,0xEDB88320,0x9ABFB3B6,0x03B6E20C,0x74B1D29A,0xEAD54739,0x9DD277AF,0x04DB2615,0x73DC1683,0xE3630B12,0x94643B84,0x0D6D6A3E,0x7A6A5AA8,0xE40ECF0B,0x9309FF9D,0x0A00AE27,0x7D079EB1,0xF00F9344,0x8708A3D2,0x1E01F268,0x6906C2FE,0xF762575D,0x806567CB,0x196C3671,0x6E6B06E7,0xFED41B76,0x89D32BE0,0x10DA7A5A,0x67DD4ACC,0xF9B9DF6F,0x8EBEEFF9,0x17B7BE43,0x60B08ED5,0xD6D6A3E8,0xA1D1937E,0x38D8C2C4,0x4FDFF252,0xD1BB67F1,0xA6BC5767,0x3FB506DD,0x48B2364B,0xD80D2BDA,0xAF0A1B4C,0x36034AF6,0x41047A60,0xDF60EFC3,0xA867DF55,0x316E8EEF,0x4669BE79,0xCB61B38C,0xBC66831A,0x256FD2A0,0x5268E236,0xCC0C7795,0xBB0B4703,0x220216B9,0x5505262F,0xC5BA3BBE,0xB2BD0B28,0x2BB45A92,0x5CB36A04,0xC2D7FFA7,0xB5D0CF31,0x2CD99E8B,0x5BDEAE1D,0x9B64C2B0,0xEC63F226,0x756AA39C,0x026D930A,0x9C0906A9,0xEB0E363F,0x72076785,0x05005713,0x95BF4A82,0xE2B87A14,0x7BB12BAE,0x0CB61B38,0x92D28E9B,0xE5D5BE0D,0x7CDCEFB7,0x0BDBDF21,0x86D3D2D4,0xF1D4E242,0x68DDB3F8,0x1FDA836E,0x81BE16CD,0xF6B9265B,0x6FB077E1,0x18B74777,0x88085AE6,0xFF0F6A70,0x66063BCA,0x11010B5C,0x8F659EFF,0xF862AE69,0x616BFFD3,0x166CCF45,0xA00AE278,0xD70DD2EE,0x4E048354,0x3903B3C2,0xA7672661,0xD06016F7,0x4969474D,0x3E6E77DB,0xAED16A4A,0xD9D65ADC,0x40DF0B66,0x37D83BF0,0xA9BCAE53,0xDEBB9EC5,0x47B2CF7F,0x30B5FFE9,0xBDBDF21C,0xCABAC28A,0x53B39330,0x24B4A3A6,0xBAD03605,0xCDD70693,0x54DE5729,0x23D967BF,0xB3667A2E,0xC4614AB8,0x5D681B02,0x2A6F2B94,0xB40BBE37,0xC30C8EA1,0x5A05DF1B,0x2D02EF8D];\
function crc32(str)\
{\
var i;\
var sl=str.length;\
var crc=0;\
var ti;\
for(i=0;i<sl;++i)\
{\
ti=(crc^str.charCodeAt(i))&255;\
crc=(crc>>>8)^crc32x[ti];\
}\
return crc;\
}\
function askDeleteFile(fn)\
{\
var x=window.confirm('Are you sure you want to delete \"'+fn+'\"?');\
if (x)\
{\
var xmlhttpdel;\
xmlhttpdel=GetXmlHttpObject();\
if (xmlhttpdel!=null)\
{\
var url='" NAME "?func=" VALSTRINGIFY(FUNC_AJAX_DELETE) "&amp;fn='+fn;\
xmlhttpdel.open('POST',url,true);\
xmlhttpdel.send(null);\
}\
}\
}\
function askRenameFile(fn)\
{\
var newfn=prompt('Please enter the new filename',fn);\
if(newfn!=null && newfn!='' && newfn!=fn)\
{\
var x=window.confirm('Are you sure you want to rename \"'+fn+'\" to \"'+newfn+'\"?');\
if (x)\
{\
var xmlhttprn;\
xmlhttprn=GetXmlHttpObject();\
if (xmlhttprn!=null)\
{\
var url='" NAME "?func=" VALSTRINGIFY(FUNC_AJAX_RENAME) "&amp;fn='+fn+'&amp;newfn='+newfn;\
xmlhttprn.open('POST',url,true);\
xmlhttprn.send(null);\
}\
}\
}\
}\
function initCatchDrop(dropdir)\
{\
uploaddir=dropdir;\
window.addEventListener(\"dragenter\", dragenter, true);\
droplist=document.getElementById(\"droplist\");\
droparea=document.getElementById(\"droparea\");\
window.addEventListener(\"dragleave\", dragleave, true);\
droparea.addEventListener(\"dragover\", dragover, true);\
droparea.addEventListener(\"drop\", drop, true);\
}\
function dragenter(e)\
{\
e.preventDefault();\
droparea.setAttribute(\"dragenter\", true);\
}\
function dragleave(e)\
{\
droparea.removeAttribute(\"dragenter\");\
}\
function dragover(e)\
{\
e.preventDefault();\
}\
function drop(e)\
{\
e.preventDefault();\
var dt=e.dataTransfer;\
var files=dt.files;\
handleFiles(files);\
}\
function handleFiles(files)\
{\
var i;\
var slot=numdrop;\
var file;\
for(i=0;i<files.length;++i)\
{\
file=files[i];\
dropname[slot]=file.name;\
dropstate[slot]=0;\
dropresults[slot]='Loading into Browser';\
dropreader[slot]=new FileReader();\
++numdrop;\
updateList();\
dropreader[slot].onloadend=dropLoaded;\
dropreader[slot].readAsBinaryString(file);\
}\
}\
function dropLoaded()\
{\
updateList();\
}\
function updateList()\
{\
var i;\
var h='';\
var data;\
h='<table>';\
for(i=0;i<numdrop;++i)\
{\
if(dropstate[i]==0 && dropreader[i].readyState==2 && uploadslot<0)\
{\
xmlhttp=GetXmlHttpObject();\
if (xmlhttp!=null)\
{\
uploadslot=i;\
dropstate[i]=1;\
dropresults[i]='Uploading';\
data=dropreader[i].result;\
xmlhttp.onreadystatechange=stateChanged;\
var url='" NAME "?func=" VALSTRINGIFY(FUNC_AJAX_SAVE) "&amp;fn='+uploaddir+dropname[i]+'&amp;len='+data.length+'&amp;crc='+crc32(data);\
xmlhttp.open('POST',url);\
xmlhttp.overrideMimeType('text/plain; charset=x-user-defined-binary');\
xmlhttp.sendAsBinary(data);\
}\
}\
h=h+'<tr><td>';\
if(dropstate[i]<2)\
{\
h=h+'<span class=\"busy\"></span>';\
}\
h=h+dropname[i]+'</td><td>'+dropresults[i]+'</td></tr>';\
}\
h=h+'</table>';\
droplist.innerHTML=h;\
}\
function stateChanged()\
{\
if (xmlhttp.readyState==4)\
{\
dropstate[uploadslot]=2;\
dropresults[uploadslot]=xmlhttp.responseText;\
uploadslot=-1;\
updateList();\
}\
}\
function GetXmlHttpObject()\
{\
if (window.XMLHttpRequest)\
  return new XMLHttpRequest();\
if (window.ActiveXObject)\
  return new ActiveXObject(\"Microsoft.XMLHTTP\");\
return null;\
\n}"};

const char g_javascript_shell[]={"\
var hb=0;\
var hbdelay=1;\
var rawline=[];\
var ajaxtime=1;\
var lineorder=[];\
var line=[];\
var ccindex=0;\
var cursorclass=[];\
var xmlhttp;\
var xmlhttpbusy=false;\
var tosend='';\
var ctrl=0;\
var topline=0;\
var output='';\
var specialkey='';\
var kdown=[];\
var b64='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';\
var encstring;\
var encindex;\
var encbits;\
var encnumbits;\
function db64init(enc)\
{\
encstring=enc;\
encindex=0;\
encnumbits=0;\
encbits=0;\
}\
function db64get(nb)\
{\
var retbits;\
var mask;\
do{\
if(encnumbits>=nb)\
{\
mask=(1<<nb)-1;\
retbits=(encbits>>(encnumbits-nb))&mask;\
encnumbits-=nb;\
if(encnumbits==0)\
{\
encbits=0;\
}\
else\
{\
mask=(1<<encnumbits)-1;\
encbits=encbits&mask;\
}\
return retbits;\
}\
if(encindex<encstring.length)\
{\
encbits=(encbits<<6)|b64.indexOf(encstring.charAt(encindex++));\
}\
else\
{\
encbits=encbits<<6;\
}\
encnumbits=encnumbits+6;\
}while(1);\
}\
function doSend()\
{\
var url;\
if(xmlhttpbusy==false)\
{\
xmlhttp=GetXmlHttpObject();\
if (xmlhttp!=null)\
{\
hb=0;\
if(tosend=='')\
{\
url='" NAME "?cmd=%00&amp;time='+ajaxtime;\
}\
else\
{\
url='" NAME "?cmd='+encodeURIComponent(tosend)+'&amp;time='+ajaxtime;\
tosend='';\
}\
xmlhttpbusy=true;\
document.getElementById('status').setAttribute('class', 'busy');\
xmlhttp.onreadystatechange=stateChanged;\
xmlhttp.open('GET',url,true);\
xmlhttp.send(null);\
}\
}\
}\
function incHeartbeat()\
{\
++hb;\
if(hb>=hbdelay)\
{\
doSend()\
}\
}\
var hbtimer=setInterval('incHeartbeat()', 250);\
function flashCursor()\
{\
var cursor;\
cursor=document.getElementById('cursor');\
if(cursor)\
{\
ccindex^=1;\
cursor.setAttribute('class', cursorclass[ccindex]);\
}\
}\
var ctimer=setInterval('flashCursor()', 500);\
function keyud(e,v)\
{\
var keyCode=e.keyCode?e.keyCode:e.which;\
specialkey='';\
if(keyCode==17)\
{\
ctrl=v;\
return true;\
}\
if(v)\
{\
if(ctrl)\
{\
if(keyCode>=64)\
specialkey=String.fromCharCode(keyCode-64);\
}\
else if(keyCode<kdown.length)\
{\
specialkey=kdown[keyCode];\
}\
}\
if(specialkey.length)\
{\
tosend=tosend+specialkey;\
hbdelay=1;\
doSend();\
return false;\
}\
return true;\
}\
function sendkey(e)\
{\
var charCode=e.charCode||e.keyCode||e.which;\
var ss;\
if(specialkey.length || ctrl)\
return false;\
if(charCode==13)\
{\
charCode=10;\
}\
ss=String.fromCharCode(charCode);\
tosend=tosend+ss;\
hbdelay=1;\
doSend();\
return false;\
}\
function startShell()\
{\
var i;\
for(i=0;i<256;++i)\
{\
kdown[i]='';\
}\
kdown[8]='\x08';\
kdown[33]='\x1b[5~';\
kdown[34]='\x1b[6~';\
kdown[35]='\x1b[4~';\
kdown[36]='\x1b[1~';\
kdown[37]='\x1b[D';\
kdown[38]='\x1b[A';\
kdown[39]='\x1b[C';\
kdown[40]='\x1b[B';\
kdown[45]='\x1b[2~';\
kdown[46]='\x1b[3~';\
kdown[112]='\x1b[[A';\
kdown[113]='\x1b[[B';\
kdown[114]='\x1b[[C';\
kdown[115]='\x1b[[D';\
kdown[116]='\x1b[[E';\
kdown[117]='\x1b[[17~';\
kdown[118]='\x1b[[18~';\
kdown[119]='\x1b[[19~';\
kdown[120]='\x1b[[20~';\
kdown[121]='\x1b[[21~';\
kdown[122]='\x1b[[23~';\
kdown[123]='\x1b[[24~';\
for(i=0;i<" VALSTRINGIFY(NUMLINES) ";++i)\
{\
lineorder[i]=i;\
line[i]='';\
}\
updateOutput();\
}\
function shutdownDaemon()\
{\
xmlhttp=GetXmlHttpObject();\
if (xmlhttp==null)\
{\
return false;\
}\
hb=0;\
xmlhttpbusy=true;\
xmlhttp.onreadystatechange=stateChanged;\
var url='" NAME "?func=" VALSTRINGIFY(FUNC_AJAX_SHUTDOWN) "';\
xmlhttp.open('GET',url,true);\
xmlhttp.send(null);\
}\
function updateOutput()\
{\
var ln;\
var sln;\
var b;\
var bl=0;\
output='';\
for(ln=0;ln<lineorder.length;ln++)\
{\
sln=(lineorder[(ln+topline)%lineorder.length]);\
if(line[sln]=='')\
{\
++bl;\
}\
else\
{\
for(b=0;b<bl;++b)\
{\
output=output+'<br>';\
}\
output=output+line[sln];\
bl=1;\
}\
}\
var outelem=document.getElementById('output');\
outelem.innerHTML=output;\
outelem.scrollTop=outelem.scrollHeight-outelem.clientHeight;\
}\
function getClass(style,reverse)\
{\
var c='';\
if(style&" VALSTRINGIFY(STYLE_BOLD) ")\
c=c+'bd ';\
if(style&" VALSTRINGIFY(STYLE_UNDERLINE) ")\
c=c+'ul ';\
if(style&" VALSTRINGIFY(STYLE_BLINK) ")\
c=c+'bl ';\
if(style&(" VALSTRINGIFY(STYLE_DOUBLETOP) "|" VALSTRINGIFY(STYLE_DOUBLEBOTTOM) "))\
c=c+'ds ';\
if(reverse)\
{\
c=c+'b'+((style>>" VALSTRINGIFY(STYLE_BITSUSED) ")&" VALSTRINGIFY(STYLE_COLORMASK) ")+' f'+((style>>(" VALSTRINGIFY(STYLE_BITSUSED) "+" VALSTRINGIFY(STYLE_COLORBITS) "))&" VALSTRINGIFY(STYLE_COLORMASK) ");\
}\
else\
{\
c=c+'f'+((style>>" VALSTRINGIFY(STYLE_BITSUSED) ")&" VALSTRINGIFY(STYLE_COLORMASK) ")+' b'+((style>>(" VALSTRINGIFY(STYLE_BITSUSED) "+" VALSTRINGIFY(STYLE_COLORBITS) "))&" VALSTRINGIFY(STYLE_COLORMASK) ");\
}\
return c;\
}\
function stateChanged()\
{\
if (xmlhttp.readyState==4)\
{\
if(xmlhttp.status==200)\
{\
var response=xmlhttp.responseText;\
var i;\
var c;\
var ln;\
var sln;\
var ltext;\
var lstyle;\
var style;\
var instyle=0;\
var lineschanged;\
var s=0;\
if(response.length==0)\
{\
if(hbdelay<16)\
{\
hbdelay*=2;\
}\
}\
else\
{\
hbdelay=1;\
db64init(response);\
ajaxtime=db64get(" VALSTRINGIFY(MAXTIMEBITS) ");\
topline=db64get(" VALSTRINGIFY(NUMLINEBITS) ");\
if(db64get(1)==1)\
{\
lineschanged=db64get(" VALSTRINGIFY(NUMLINEBITS) ");\
for(i=0;i<lineschanged;++i)\
{\
ln=db64get(" VALSTRINGIFY(NUMLINEBITS) ");\
sln=db64get(" VALSTRINGIFY(NUMLINEBITS) ");\
lineorder[ln]=sln;\
}\
}\
lineschanged=db64get(" VALSTRINGIFY(NUMLINEBITS) ");\
for(i=0;i<lineschanged;++i)\
{\
ln=db64get(" VALSTRINGIFY(NUMLINEBITS) ");\
ltext='';\
s=0;\
do{\
if(db64get(1)==0)\
{\
c=db64get(7);\
}\
else\
{\
c=db64get(16);\
}\
if(c)\
{\
if(db64get(1)==0)\
{\
style=db64get(" VALSTRINGIFY(STYLE_BITSUSED) "+" VALSTRINGIFY(STYLE_COLORBITS) "+" VALSTRINGIFY(STYLE_COLORBITS) ");\
}\
if(s==0)\
{\
lstyle=style;\
s=1;\
}\
else if(style!=lstyle)\
{\
while(instyle)\
{\
ltext=ltext+'</span>';\
--instyle;\
}\
lstyle=style;\
}\
if(instyle==0)\
{\
instyle=1;\
if(style&" VALSTRINGIFY(STYLE_CURSOR) ")\
{\
cursorclass[0]=getClass(style,0);\
cursorclass[1]=getClass(style,1);\
ltext=ltext+'<span id=\"cursor\" class=\"'+cursorclass[ccindex]+'\">';\
}\
else\
{\
ltext=ltext+'<span class=\"'+getClass(style,style&" VALSTRINGIFY(STYLE_REVERSE) ")+'\">';\
}\
if(style&" VALSTRINGIFY(STYLE_DOUBLEBOTTOM) ")\
{\
ltext=ltext+'<span class=\"db\">';\
instyle=2;\
}\
}\
switch(c)\
{\
case 32:\
ltext=ltext+'&nbsp;';\
break;\
case 38:\
ltext=ltext+'&amp;';\
break;\
case 60:\
ltext=ltext+'&lt;';\
break;\
case 62:\
ltext=ltext+'&gt;';\
break;\
default:\
ltext=ltext+String.fromCharCode(c);\
break;\
}\
}\
}while(c);\
while(instyle)\
{\
ltext=ltext+'</span>';\
--instyle;\
}\
line[ln]=ltext;\
}\
updateOutput();\
}\
}\
document.getElementById('status').setAttribute('class', 'done');\
xmlhttpbusy=false;\
}\
}\
\
function GetXmlHttpObject()\
{\
if (window.XMLHttpRequest)\
  return new XMLHttpRequest();\
if (window.ActiveXObject)\
  return new ActiveXObject(\"Microsoft.XMLHTTP\");\
return null;\
}\
function doPaste()\
{\
var pt=prompt('Paste into this box and press OK');\
document.getElementById('paste').blur();\
if(pt!=null && pt!='')\
{\
tosend=tosend+pt;\
hbdelay=1;\
doSend();\
}\
}"};

typedef struct
{
	int head;
	int tail;
	unsigned char buffer[4096];
}SHAREBLOCK_DEF;

typedef struct
{
	int numbits;
	unsigned char bits;
}SHBITS_DEF;

typedef struct
{
	unsigned int ready;		/* ready sentinel 0xdeadbeef */
	int killdaemon;			/* cgi-bin tells the daemon to shutdown */
	SHAREBLOCK_DEF cgitod;	/* cgi->daemon */
	int cgiready;			/* cgi is ready to receive */
	int cginotdone;			/* cgi is not done receiving */
	SHAREBLOCK_DEF dtocgi;	/* daemon->cgi */
}SHAREDMEM_DEF;

bool SH_IsEmpty(SHAREBLOCK_DEF *b)
{
	if(b->head==b->tail)
		return(true);			//yes it is empty
	return(false);				//nope, not empty
}

unsigned char SH_Get(SHAREBLOCK_DEF *b)
{
	unsigned char c;
	int nexthead;

	c=b->buffer[b->head];

	nexthead=b->head+1;
	if(nexthead==sizeof(b->buffer))
		nexthead=0;
	b->head=nexthead;
	return(c);
}

void SH_Put(SHAREBLOCK_DEF *b,unsigned char byte)
{
	int nexttail;

	do
	{
		nexttail=b->tail+1;
		if(nexttail==sizeof(b->buffer))
			nexttail=0;

		if(b->head!=nexttail)
			break;				/* ok we have room! */
		/* buffer is full so sleep a bit */
		sleepms(10);
	}while(1);

	//dlog("SH_Put byte=%d at index %d\n",byte,b->tail);
	b->buffer[b->tail]=byte;
	b->tail=nexttail;
}

void SH_PutBitsInit(SHBITS_DEF *bb)
{
	bb->numbits=0;
	bb->bits=0;
}

void SH_PutBits(SHAREBLOCK_DEF *b,SHBITS_DEF *bb,unsigned int bits,unsigned int numbits)
{
	unsigned long long allbits;	/* long long is so we can handle more than 32 bits */

	/* all bits = previous bits in the buffer plus the newly added bits */
	allbits=bb->bits;
	allbits<<=numbits;
	allbits|=bits;
	numbits+=bb->numbits;

	while(numbits>=8)
	{
		/* we have enough to write out */
		SH_Put(b,(unsigned char)(allbits>>(numbits-8)));
		numbits-=8;
	}
	bb->bits=(unsigned char)allbits;	/* numbits is <8 so we can safely downcast this to a unsigned char */
	bb->numbits=numbits;
}

void SH_PutBitsFlush(SHAREBLOCK_DEF *b,SHBITS_DEF *bb)
{
	if(bb->numbits)
		SH_PutBits(b,bb,0,8-bb->numbits);
}

void SH_Write(SHAREBLOCK_DEF *b,const void *data,unsigned int len)
{
	unsigned int i;
	const unsigned char *cp=(const unsigned char *)data;
	for(i=0;i<len;++i)
		SH_Put(b,*(cp++));
}

enum
{
IMAGE_UNKNOWN,
IMAGE_JPG,
IMAGE_GIF,
IMAGE_PNG,
IMAGE_ICO,
IMAGE_BMP
};

unsigned int getImageType(const char *fn)
{
	FILE *fp;
	unsigned int type=IMAGE_UNKNOWN;

	/* determine the type, first check for an image */
	unsigned char testread[5];

	fp=fopen(fn,"rb");
	if(fp)
	{
		if(fread(testread,1,4,fp)==4)
		{
			testread[4]=0;
			if(!strcmp((char *)testread,"GIF8"))
				type=IMAGE_GIF;
			else if(testread[0]==0xff && testread[1]==0xd8)
				type=IMAGE_JPG;
			else if(testread[0]==0x89 && testread[1]==0x50 && testread[2]==0x4e && testread[3]==0x47)
				type=IMAGE_PNG;
			else if(testread[0]==0x00 && testread[1]==0x00 && testread[2]==0x01 && testread[3]==0x00)
				type=IMAGE_ICO;
			else if(testread[0]==0x42 && testread[1]==0x4d)
				type=IMAGE_BMP;
		}
		fclose(fp);
	}
	return(type);
}

/* our favicon */
static const unsigned char g_icon[1286]={71,73,70,56,57,97,28,0,16,0,247,0,0,0,0,0,255,255,255,221,234,242,220,233,241,97,82,83,116,101,102,109,
97,98,101,90,91,136,123,124,140,129,130,103,94,95,95,80,82,101,86,88,110,94,96,195,192,193,143,136,139,97,89,93,
152,149,151,203,200,202,127,116,124,102,97,101,106,105,108,161,158,170,177,177,183,85,85,87,186,187,194,197,198,202,198,200,
205,121,122,124,210,223,237,213,225,238,196,206,217,204,218,232,113,116,119,138,162,185,184,202,218,131,141,150,207,222,236,214,
229,243,211,226,240,211,225,238,210,224,237,209,223,236,208,222,235,214,228,241,212,225,237,189,201,212,201,219,234,202,219,234,
197,214,228,208,225,239,172,186,198,203,219,233,198,214,227,212,228,242,205,221,234,209,224,237,206,221,234,212,226,238,182,193,
202,137,178,210,137,176,207,133,171,201,141,177,204,197,219,236,105,117,126,199,218,233,198,217,232,206,225,240,206,223,236,214,
230,242,218,233,245,105,110,114,98,102,105,165,169,172,142,182,209,152,174,190,186,211,228,186,210,227,138,155,167,190,212,227,
193,214,229,203,224,238,197,217,231,202,219,231,216,231,241,229,243,253,201,213,221,231,244,253,227,240,249,226,239,248,224,237,
246,212,222,229,151,158,163,135,176,201,144,186,212,141,181,206,139,176,199,143,178,200,149,182,202,182,208,224,190,213,228,203,
222,234,215,233,244,215,229,238,228,242,251,225,239,248,223,236,244,219,232,240,215,228,236,234,247,255,224,237,245,226,237,244,
148,185,207,159,194,213,89,108,119,148,178,195,167,199,218,197,219,232,195,217,230,212,228,237,214,230,239,223,239,248,225,239,
247,224,236,243,182,192,198,190,200,206,220,234,241,222,235,242,189,199,204,102,150,172,116,164,186,133,175,195,141,183,202,124,
135,140,58,70,75,157,169,174,75,83,86,107,113,115,90,92,92,66,67,67,95,96,95,52,53,51,39,41,36,49,50,46,
60,61,57,38,39,33,43,43,39,45,45,41,48,48,44,37,36,31,9,8,5,14,13,10,46,45,42,28,26,21,51,49,
44,17,16,14,21,20,18,25,24,22,48,47,45,82,81,79,46,42,36,44,41,37,54,50,45,51,45,38,10,7,4,35,
29,23,29,26,23,29,27,25,59,55,51,33,31,29,68,65,62,59,58,57,46,40,35,51,45,40,62,55,49,79,74,70,
79,75,72,62,53,47,54,47,42,32,29,27,82,71,65,90,79,73,86,76,70,124,110,102,82,75,71,89,77,71,82,72,
67,57,51,48,71,58,52,99,82,74,106,88,80,87,74,68,81,70,65,99,86,80,98,85,79,95,80,74,111,94,87,81,
69,64,107,93,87,93,81,76,103,91,86,90,80,76,67,60,57,129,109,102,105,89,83,116,99,93,112,96,90,94,81,76,
136,117,110,122,105,99,124,107,101,122,106,100,130,114,108,138,121,115,114,100,95,67,59,56,118,100,94,132,113,107,111,96,
91,41,38,37,85,71,67,127,107,101,135,115,109,129,111,106,53,46,44,97,82,78,106,90,86,105,93,90,124,112,109,63,
59,58,76,63,60,90,76,73,79,70,68,82,66,63,102,85,82,147,136,134,100,86,84,71,55,53,119,100,98,126,110,108,
101,92,91,107,98,97,85,73,72,95,82,81,92,80,79,119,109,108,168,155,154,61,45,44,84,70,69,132,117,116,94,81,
81,99,88,88,123,112,112,88,81,81,255,255,255,33,249,4,1,0,0,255,0,44,0,0,0,0,28,0,16,0,0,8,
255,0,83,220,88,129,195,134,142,20,38,164,192,64,145,66,71,141,17,57,110,148,80,65,177,136,144,20,30,82,164,104,
81,130,224,137,12,15,18,68,248,112,36,6,149,23,83,64,164,192,161,146,76,161,65,131,228,56,41,19,5,10,69,35,
23,198,65,0,101,204,158,11,34,83,194,136,25,210,65,135,201,40,132,4,9,26,20,167,78,33,66,41,88,184,40,176,
0,3,36,75,179,20,100,144,65,71,80,83,39,99,68,196,1,83,199,206,29,51,119,166,224,129,1,196,2,1,3,140,
36,61,162,212,237,91,132,12,51,196,128,249,178,196,203,143,56,102,224,8,0,68,88,192,146,31,19,8,40,88,85,105,
82,170,124,240,190,101,219,86,142,3,9,38,77,164,156,81,179,103,207,26,1,3,6,240,16,81,96,223,1,118,145,114,
181,195,39,238,155,51,96,196,124,253,138,118,207,129,4,13,26,54,112,89,163,5,208,232,6,12,236,221,155,132,235,157,
187,110,235,164,221,130,54,45,154,54,102,222,188,21,107,214,172,26,59,37,92,70,243,171,135,160,223,168,104,209,212,157,
227,67,7,110,88,176,99,211,168,73,155,246,172,150,43,81,164,96,117,153,209,195,7,5,122,9,252,97,179,70,46,220,
45,3,215,8,179,139,45,199,140,131,140,48,193,188,18,138,38,153,244,145,6,27,69,12,81,193,60,9,164,131,141,62,
230,160,178,140,50,201,232,82,139,56,200,12,35,140,45,163,108,226,8,37,17,184,49,128,0,124,96,145,4,45,242,136,
66,75,60,220,156,194,75,42,177,172,114,75,47,173,200,242,138,41,139,4,114,197,21,2,128,54,192,31,86,32,81,138,
39,156,112,226,143,42,157,88,226,10,40,136,32,18,66,35,138,24,98,136,31,88,108,177,133,145,3,0,34,64,8,157,
96,194,9,38,167,124,162,202,37,172,112,169,134,30,2,168,161,134,21,123,176,17,218,157,3,188,225,194,14,79,4,49,
199,33,137,4,177,72,34,125,96,17,218,96,69,226,169,232,0,108,172,177,197,25,85,228,65,5,26,109,252,145,197,162,
152,222,25,16,0,59};

void printfile(FILE *fp)
{
	unsigned char c;

	while(1)
	{
		c=fgetc(fp);
		if(feof(fp))
			break;
		switch(c)
		{
		case '\n':
			printf("<br>");
		break;
		case '<':
			printf("&lt;");
		break;
		case '>':
			printf("&gt;");
		break;
		case '&':
			printf("&amp;");
		break;
		default:
			printf("%c",c);
		break;
		}
	}
	fclose(fp);
}

bool compare_nocase(string first, string second)
{
	unsigned int i=0;
	while ( (i<first.length()) && (i<second.length()) )
	{
	if (tolower(first[i])<tolower(second[i]))
		return true;
	else if (tolower(first[i])>tolower(second[i]))
		return false;
	++i;
	}
	if (first.length()<second.length())
	  return true;
	return false;
}

unsigned int crc32(const char *c,unsigned int l)
{
	unsigned int i;
	unsigned int crc=0;
	unsigned int ti;
	//these MUST match the values used in the Javascript crc32 function!
	static const unsigned int crc32x[]={0x00000000,0x77073096,0xEE0E612C,0x990951BA,0x076DC419,0x706AF48F,0xE963A535,0x9E6495A3,0x0EDB8832,0x79DCB8A4,0xE0D5E91E,0x97D2D988,0x09B64C2B,0x7EB17CBD,0xE7B82D07,0x90BF1D91,0x1DB71064,0x6AB020F2,0xF3B97148,0x84BE41DE,0x1ADAD47D,0x6DDDE4EB,0xF4D4B551,0x83D385C7,0x136C9856,0x646BA8C0,0xFD62F97A,0x8A65C9EC,0x14015C4F,0x63066CD9,0xFA0F3D63,0x8D080DF5,0x3B6E20C8,0x4C69105E,0xD56041E4,0xA2677172,0x3C03E4D1,0x4B04D447,0xD20D85FD,0xA50AB56B,0x35B5A8FA,0x42B2986C,0xDBBBC9D6,0xACBCF940,0x32D86CE3,0x45DF5C75,0xDCD60DCF,0xABD13D59,0x26D930AC,0x51DE003A,0xC8D75180,0xBFD06116,0x21B4F4B5,0x56B3C423,0xCFBA9599,0xB8BDA50F,0x2802B89E,0x5F058808,0xC60CD9B2,0xB10BE924,0x2F6F7C87,0x58684C11,0xC1611DAB,0xB6662D3D,0x76DC4190,0x01DB7106,0x98D220BC,0xEFD5102A,0x71B18589,0x06B6B51F,0x9FBFE4A5,0xE8B8D433,0x7807C9A2,0x0F00F934,0x9609A88E,0xE10E9818,0x7F6A0DBB,0x086D3D2D,0x91646C97,0xE6635C01,0x6B6B51F4,0x1C6C6162,0x856530D8,0xF262004E,0x6C0695ED,0x1B01A57B,0x8208F4C1,0xF50FC457,0x65B0D9C6,0x12B7E950,0x8BBEB8EA,0xFCB9887C,0x62DD1DDF,0x15DA2D49,0x8CD37CF3,0xFBD44C65,0x4DB26158,0x3AB551CE,0xA3BC0074,0xD4BB30E2,0x4ADFA541,0x3DD895D7,0xA4D1C46D,0xD3D6F4FB,0x4369E96A,0x346ED9FC,0xAD678846,0xDA60B8D0,0x44042D73,0x33031DE5,0xAA0A4C5F,0xDD0D7CC9,0x5005713C,0x270241AA,0xBE0B1010,0xC90C2086,0x5768B525,0x206F85B3,0xB966D409,0xCE61E49F,0x5EDEF90E,0x29D9C998,0xB0D09822,0xC7D7A8B4,0x59B33D17,0x2EB40D81,0xB7BD5C3B,0xC0BA6CAD,0xEDB88320,0x9ABFB3B6,0x03B6E20C,0x74B1D29A,0xEAD54739,0x9DD277AF,0x04DB2615,0x73DC1683,0xE3630B12,0x94643B84,0x0D6D6A3E,0x7A6A5AA8,0xE40ECF0B,0x9309FF9D,0x0A00AE27,0x7D079EB1,0xF00F9344,0x8708A3D2,0x1E01F268,0x6906C2FE,0xF762575D,0x806567CB,0x196C3671,0x6E6B06E7,0xFED41B76,0x89D32BE0,0x10DA7A5A,0x67DD4ACC,0xF9B9DF6F,0x8EBEEFF9,0x17B7BE43,0x60B08ED5,0xD6D6A3E8,0xA1D1937E,0x38D8C2C4,0x4FDFF252,0xD1BB67F1,0xA6BC5767,0x3FB506DD,0x48B2364B,0xD80D2BDA,0xAF0A1B4C,0x36034AF6,0x41047A60,0xDF60EFC3,0xA867DF55,0x316E8EEF,0x4669BE79,0xCB61B38C,0xBC66831A,0x256FD2A0,0x5268E236,0xCC0C7795,0xBB0B4703,0x220216B9,0x5505262F,0xC5BA3BBE,0xB2BD0B28,0x2BB45A92,0x5CB36A04,0xC2D7FFA7,0xB5D0CF31,0x2CD99E8B,0x5BDEAE1D,0x9B64C2B0,0xEC63F226,0x756AA39C,0x026D930A,0x9C0906A9,0xEB0E363F,0x72076785,0x05005713,0x95BF4A82,0xE2B87A14,0x7BB12BAE,0x0CB61B38,0x92D28E9B,0xE5D5BE0D,0x7CDCEFB7,0x0BDBDF21,0x86D3D2D4,0xF1D4E242,0x68DDB3F8,0x1FDA836E,0x81BE16CD,0xF6B9265B,0x6FB077E1,0x18B74777,0x88085AE6,0xFF0F6A70,0x66063BCA,0x11010B5C,0x8F659EFF,0xF862AE69,0x616BFFD3,0x166CCF45,0xA00AE278,0xD70DD2EE,0x4E048354,0x3903B3C2,0xA7672661,0xD06016F7,0x4969474D,0x3E6E77DB,0xAED16A4A,0xD9D65ADC,0x40DF0B66,0x37D83BF0,0xA9BCAE53,0xDEBB9EC5,0x47B2CF7F,0x30B5FFE9,0xBDBDF21C,0xCABAC28A,0x53B39330,0x24B4A3A6,0xBAD03605,0xCDD70693,0x54DE5729,0x23D967BF,0xB3667A2E,0xC4614AB8,0x5D681B02,0x2A6F2B94,0xB40BBE37,0xC30C8EA1,0x5A05DF1B,0x2D02EF8D};

	for(i=0;i<l;++i)
	{
		ti=(crc^*(c++))&255;
		crc=(crc>>8)^crc32x[ti];
	}
	return crc;
}

int startdaemon(int autostart,const char *defshell);

static const char *tabnames[]={
#if USELOGIN
	"Login",
	"Logout",
#endif
	"System",
	"Files",
	"Shell",
	"Users",
	"Mounts",
	"CPU",
	"Devices",
	"Filesystems",
	"Interfaces",
	"Ports",
	"Processes",
	"HTTP",
#if DOTEST
	"Test",
#endif
	"Credits"
};

int mycgi(void)
{
	int i;
	int tabnum=TAB_SYSTEM;
	const char *cp;
	Parms parms;
	Cookies cookies;
	const char *homedir=defhomedir;
	struct stat statbuf;
	const char *viewfn=0;
	FILE *fp;
	char defshell[256];
#if USELOGIN
	bool login=false;
#endif
	bool secure=false;
	int allowtab[TAB_NUMTABS];
	int func=FUNC_UNDEFINED;

	for(i=0;i<TAB_NUMTABS;++i)
		allowtab[i]=1;

	umask(0);

	parms.Load();
	cookies.Load();

	cp=getenv("SERVER_PORT");
	if(cp)
	{
		if(cp[0]=='4')
			secure=true;
	}

	cp=parms.FindParmz("func");
	if(cp)
		func=atoi(cp);

	/* if not logged in then goto login page */
#if USELOGIN
	{
		const char *name;
		const char *epw;

		name=cookies.FindCookiez("n");
		epw=cookies.FindCookiez("p");
		if(name && epw)
		{
		    struct spwd *pw;
			struct passwd *pwe;

			/* check to see if this is still a valid name/pw pair! */
			pw = getspnam(name);
		    if (pw != NULL)
			{
				/* convert plain password to encrypted password */
			    if (!strcmp(epw, pw->sp_pwdp))
				{
					login=true;
					/* check for the user and if found then grab the shell name */
					setpwent();
					do
					{
						pwe=getpwent();
						if(!pwe)
							break;
					    if (!strcmp(name, pwe->pw_name))
						{
							/* get the shell name to use for this user */
							strcpy(defshell,pwe->pw_shell);
							//dlog("default shell for user is '%s'\n",defshell);
						}
					}while(1);
					endpwent();	/* done scan */
				}
			}
		}
	}
	if(login)
#endif
	{
		const char *cmd;

		cmd=parms.FindParmz("cmd");

		if(func==FUNC_AJAX_SHUTDOWN || cmd)
		{
			//ajax command!!
			printf("Cache-Control: no-store, must-revalidate\012");
			printf("\012");

			/* locate shared memory */
			{
				int shmid;
				key_t shmkey=USEKEY;
				SHAREDMEM_DEF *psm;
				int n;
				unsigned char c;

				/* get pointer to shared memory for communication with the daemon */
				shmid=shmget(shmkey,sizeof(SHAREDMEM_DEF),0666);
				if(shmid<0)
				{
					/* launch demon if shutdown is not true */
					if(func!=FUNC_AJAX_SHUTDOWN)
					{
						fflush(stdout);
						dlog("cgi-bin starting daemon defshell='%s'!\r\n",defshell);
						startdaemon(1,defshell);
						dlog("cgi-bin starting sleep!\r\n");
						sleepms(500);	/* wait 1/2 second for daemon to startup */
						dlog("cgi-bin waking up!\r\n");

						shmid=shmget(shmkey,sizeof(SHAREDMEM_DEF),0666);
						if(shmid<0)
							printf("ERROR start daemon!\r\n");
						else
						{
							dlog("cgi-bin startup got shared memoryid!\r\n");
							goto dstartok;
						}
					}
				}
				else
				{
dstartok:			psm=(SHAREDMEM_DEF *)shmat(shmid, 0, 0);
					if(psm==(void *)-1)
					{
						dlog("cgi-bin cannot locate shared memory!\r\n");
						printf("ERROR cannot locate shared memory (in daemon)\r\n");
					}
					else
					{
						if(func==FUNC_AJAX_SHUTDOWN)
							psm->killdaemon=1;
						else
						{
							int ajaxtime;
							int timeout=0;

							cp=parms.FindParmz("time");
							if(cp)
								ajaxtime=atoi(cp);
							else
								ajaxtime=1;

							//this would only ever happen if you have multiple browers attached at the same time
							while(psm->cgiready || psm->cginotdone)
							{
								if(++timeout==200)
								{
									//dlog("Timeout trying to connect to daemon!\r\n");
									return(0);
								}
								//dlog("busy! cgiready=%d, cginotdone=%d, let's sleep a bit!\r\n",psm->cgiready,psm->cginotdone);
								sleepms(10);
							}
							{
								EncodeBase64 enc;

								//we are not finshed talking to the daemon!
								psm->cginotdone=1;

								/* send the command */
								n=strlen(cmd);
								if(!n)	/* is this a single null ( heartbeat? ) */
									n=1;
								SH_Write(&psm->cgitod,cmd,n);

								//tell daemon that we are ready to receive! */
								psm->cgiready=ajaxtime;
								while(1)
								{
									while(SH_IsEmpty(&psm->dtocgi)==false)
									{
										c=SH_Get(&psm->dtocgi);
										//dlog("got char %d from daemon!\r\n",c);
										enc.Add(c);
									}
									/* is daemon done sending?? */
									if(psm->cgiready==0)
										break;
									/* sleep a bit so daemon can refill our buffer */
									sleepms(10);
									//dlog("wait for data from demon, so sleep a bit\r\n");
								}
								enc.Flush();
								//dlog("done command to daemon\r\n");
							}
							psm->cginotdone=0;
						}
						shmdt(psm);	/* release our hold on the shared memory */
					}
				}
			}
			return(0);
		}
		switch(func)
		{
		case FUNC_AJAX_SAVE:
		{
			const char *fn;
			const char *contents;
			FILE *f;
			unsigned int contentsize;
			unsigned int flen;
			unsigned int fcrc;
			unsigned int ccrc;

			//ajax command!!
			printf("Cache-Control: no-store, must-revalidate\012");
			printf("\012");

			/* save contents to file, this is used by both edit and drag-drop files */
			fn=parms.FindParmz("fn");
			if(!fn)
				printf("ERROR, no filename parameter!");
			else
			{
				dlog("FUNC_AJAX_SAVE\r\n",fn);

				cp=parms.FindParmz("len");
				if(!cp)
					printf("ERROR, no length parameter!");
				else
				{
					flen=(atoi(cp));
					cp=parms.FindParmz("crc");
					if(!cp)
						printf("ERROR, no crc paramater!");
					else
					{
						fcrc=(atoi(cp));
						contents=parms.GetContent();
						contentsize=parms.GetContentSize();
						if(contentsize>0 && !contents)
							printf("ERROR, no file contents!");
						else
						{
							if(flen!=contentsize)
								printf("ERROR, contentsize does not match filesize! (flen=%d,clen=%d)",flen,contentsize);
							else
							{
								/* check crc of data we recieved */
								ccrc=crc32(contents,contentsize);
								if(ccrc!=fcrc)
									printf("ERROR, data crc does not match data recieved (fcrc=%08x,ccrc=%08x)!",fcrc,ccrc);
								else
								{
									/* ok, we got enough to proceed with saving data! */
									f=fopen(fn,"wb");
									if(!f)
										printf("ERROR, unable to open file for writing!");
									else
									{
										if(fwrite(contents,1,contentsize,f)!=contentsize)
										{
											printf("ERROR, writing to file!");
											fclose(f);
										}
										else
										{
											fclose(f);

											/* open file and compare it against the content */
											f=fopen(fn,"rb");
											if(!f)
												printf("ERROR, unable to open file for verifying!");
											else
											{
												unsigned int i=0;
												while(1)
												{
													char c=fgetc(f);
													if(feof(f))
														break;
													if(i==contentsize)
													{
														/* file has more in it than we wrote! */
														++i;
														break;
													}
													if(contents[i]!=c)
														break;
													++i;
												}
												fclose(f);
												if(i!=contentsize)
													printf("ERROR, contents of file don't match what was written!");
												else
													printf("OK, (%d bytes written and verified)",contentsize);
											}
										}
									}
								}
							}
						}
					}
				}
			}
			return(0);
		}
		break;
		case FUNC_AJAX_DELETE:
		{
			const char *fn;

			//ajax command!!
			printf("Cache-Control: no-store, must-revalidate\012");
			printf("\012");

			/* filename for file to delete */
			fn=parms.FindParmz("fn");
			if(fn)
				remove(fn);
			return(0);
		}
		break;
		case FUNC_AJAX_RENAME:
		{
			const char *fn;
			const char *newfn;

			//ajax command!!
			printf("Cache-Control: no-store, must-revalidate\012");
			printf("\012");

			/* filename for file to delete */
			fn=parms.FindParmz("fn");
			newfn=parms.FindParmz("newfn");
			if(fn && newfn)
				rename(fn,newfn);
			return(0);
		}
		break;
		case FUNC_GET:
		{
			const char *fn;

			fn=parms.FindParmz("fn");
			if(fn)
			{
				switch(getImageType(fn))
				{
				case IMAGE_JPG:
					printf("Content-type: image/jpeg\012");
				break;
				case IMAGE_GIF:
					printf("Content-type: image/gif\012");
				break;
				case IMAGE_PNG:
					printf("Content-type: image/png\012");
				break;
				case IMAGE_ICO:
					printf("Content-type: image/vnd.microsoft.icon\012");
				break;
				case IMAGE_BMP:
					printf("Content-type: image/bmp\012");
				break;
				default:
					printf("Content-type: text/html\012");
					/* huh? */
				break;
				}
				printf("Cache-Control: no-store, must-revalidate\012");
				printf("\012");
				fp=fopen(fn,"rb");
				if(fp)
				{
					while(1)
					{
						unsigned char c=fgetc(fp);
						if(feof(fp))
							break;
						printf("%c",c);
					}
					fclose(fp);
				}
			}
			/* return ok or error */
			return(0);
		}
		break;
		case FUNC_GETICON:
			printf("Content-type: image/gif\012");
			printf("\012");
			for(i=0;i<(int)(sizeof(g_icon));++i)
				printf("%c",g_icon[i]);
			/* return ok or error */
			return(0);
		break;
		}
	}
	printf("Content-type: text/html\012");
	printf("Cache-Control: no-store, must-revalidate\012");
	cp=getenv("HTTP_IF_MODIFIED_SINCE");
	if(cp)
	{

	}

#if USELOGIN
	if(!login)
	{
		tabnum=TAB_LOGIN;
		if(func==FUNC_LOGIN)
		{
			const char *name;
			const char *plainpw;
			const char *stay;
			bool loginfailed=true;

			name=parms.FindParmz("name");
			plainpw=parms.FindParmz("password");
			stay=parms.FindParmz("stay");

			/* in order to slow down any automated bots we will sleep a bit before we respond */
			sleep(3);

			/* make sure we have both */
			if(name && plainpw)
			{
				if(strlen(name) && strlen(plainpw))
				{
					/* is this a valid login for this machine?, let's try using it */
				    char *epasswd;
				    struct spwd *pw;

					pw = getspnam(name);
				    if (pw == NULL)
						dlog("cannot find user '%s' in password list!\r\n",name);
					else
					{
						/* convert plain password to encrypted password */
					    epasswd = crypt(plainpw, pw->sp_pwdp);
						dlog("checking password name='%s' rawpw='%s', epw='%s', storedepw='%s'!\r\n",name,plainpw,epasswd,pw->sp_pwdp);
					    if (!strcmp(epasswd, pw->sp_pwdp))
						{
							/* save a cookie with the name and encrypted password */
							tabnum=TAB_SYSTEM;
							login=true;
							cookies.SetCookie("n",name,stay);
							cookies.SetCookie("p",epasswd,stay);
							loginfailed=false;
						}
					}
				}
			}
		}
	}
	else if(func==FUNC_LOGOUT)
	{
		tabnum=TAB_LOGIN;
		login=false;
		cookies.DeleteCookie("n");
		cookies.DeleteCookie("p");
	}
#endif
	printf("\012");
	printf("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\">\n");
	printf("<html lang=\"en\">");
	printf("<head>");

	//css style here!
	printf("<style type=\"text/css\">");
	printf("/*<![CDATA[*/");
	printf(".cb {clear:both;}");
	printf(".fl {float:left;}");
	/* curved corners */
	printf(".cctl,.ebutton,.dbutton {border-top-left-radius:0.5em;-moz-border-radius-topleft:0.5em;-webkit-border-top-left-radius:0.5em;}");
	printf(".cctr,.ebutton,.dbutton {border-top-right-radius:0.5em;-moz-border-radius-topright:0.5em;-webkit-border-top-right-radius:0.5em;}");
	printf(".ccbl,.ebutton,.dbutton {border-bottom-left-radius:0.5em;-moz-border-radius-bottomleft:0.5em;-webkit-border-bottom-left-radius:0.5em;}");
	printf(".ccbr,.ebutton,.dbutton {border-bottom-right-radius:0.5em;-moz-border-radius-bottomright:0.5em;-webkit-border-bottom-right-radius:0.5em;}");

	printf(".tab0,.ebutton {border:solid 1px #11e;background-color:#aaf;}");
	printf(".tab1 {border:solid 1px #11e;background-color:#33c;}");	//current tab
	printf(".dbutton {border:solid 1px #eee;background-color:#666;font-weight:700;color:#999;text-decoration:none;}");
	printf(".tabl0,.klink,.ebutton,.b {font-weight:700;color:black}");
	printf(".tabl0,.klink,.ebutton {text-decoration:none;}");
	printf(".tabl1 {font-weight:700;color:white;text-decoration:none;}");
	printf(".tabl0:hover,.tabl1:hover,.klink:hover,.ebutton:hover {text-decoration:underline !important;cursor: pointer}");
	printf(".folder {display:inline-block;width:16px;height:13px;background-image:url();margin-left:0.5em;margin-right:8px;}");
	printf(".file {display:inline-block;width:11px;height:15px;background-image:url();margin-left:0.5em;margin-right:13px;}");
	printf(".walrus {display:inline-block;width:278px;height:132px;background-image:url();margin-top:0.5em;margin-right:0.5em}");
	printf(".box {padding-left:0.5em;border:solid 1px #11e;background-color:#eef;color:black;}");
	printf(".done,.busy {display:inline-block;width:16px;height:16px;}");
	printf(".busy {background-image:url();}");
	printf("#output,#input,.mono {font-family:Courier, monospace;line-height:1.2;}");
	printf("#output,#editfile {width:100%%;height:" VALSTRINGIFY(NUMSHOWLINESEM) "em;color:white;background-color:black;}");
	printf("#input {border:0px;padding:0px;margin:0px;width:px;height:0px;color:black;}");
	printf("#output {white-space:nowrap;overflow:scroll;}");
	printf("#name,#password {width:20em;}");
	printf("#login {margin-top:0.5em;}");
	printf(".nb {border:none;}");
	printf(".big {font-size:500%%;}");
	printf("#droparea {margin-left:-0.5em;padding-left:0.5em;border:solid 1px #e11;background-color:#fdd;}");

	printf(".ds {display:inline-block;vertical-align:bottom;overflow:hidden;font-size:2em;line-height:1.1em;height:0.6em;}");
	printf(".db {position:relative; top:-0.6em;text-decoration:inherit;}");
	printf(".bl {text-decoration:blink;}");
	printf(".ul {text-decoration:underline;}");
	printf(".bd {font-weight:700;}");
	printf(".f0 {color:black;}");
	printf(".f1 {color:red;}");
	printf(".f2 {color:green;}");
	printf(".f3 {color:yellow;}");
	printf(".f4 {color:blue;}");
	printf(".f5 {color:magenta;}");
	printf(".f6 {color:cyan;}");
	printf(".f7 {color:white;}");
	printf(".b0 {background-color:black;}");
	printf(".b1 {background-color:red;}");
	printf(".b2 {background-color:green;}");
	printf(".b3 {background-color:yellow;}");
	printf(".b4 {background-color:blue;}");
	printf(".b5 {background-color:magenta;}");
	printf(".b6 {background-color:cyan;}");
	printf(".b7 {background-color:white;}");
	
	printf("/*]]>*/");
	printf("</style>");

#if USELOGIN
	if(login)
#endif
	{
		if(func==FUNC_VIEW)
		{
			viewfn=parms.FindParmz("fn");
			if(!viewfn)
				func=FUNC_BROWSE;
			tabnum=TAB_FILES;
		}
		else if(func==FUNC_BROWSE)
		{
			cp=parms.FindParmz("dir");
			if(cp)
				homedir=cp;
			tabnum=TAB_FILES;
		}
		else
		{
			cp=parms.FindParmz("tab");
			if(cp)
			{
				tabnum=atoi(cp);
				if(tabnum>=TAB_NUMTABS)
					tabnum=TAB_SYSTEM;
			}
			else
				tabnum=TAB_SYSTEM;
		}
	}
	//Javascript goes next
	{
		static const char *js;

		switch(tabnum)
		{
		case TAB_SHELL:
			js=g_javascript_shell;
		break;
		case TAB_FILES:
			if(func==FUNC_VIEW)
				js=g_javascript_editfile;
			else
				js=g_javascript_drop;
		break;
		default:
			js=0;
		break;
		}

		if(js)
			printf("<script type=\"text/javascript\">/*<![CDATA[*/%s/*]]>*/</script>",js);
	}

	printf("<title>" NAME " " VERSION "</title>");
	printf("<link rel=\"shortcut icon\" href=\"" NAME "?func=" VALSTRINGIFY(FUNC_GETICON) "\">");
	printf("<link rel=\"icon\" href=\"" NAME "?func=" VALSTRINGIFY(FUNC_GETICON) "\">");
	switch(tabnum)
	{
	case TAB_SHELL:
		printf("</head><body onload=\"startShell();\" onbeforeunload=\"shutdownDaemon();\" onunload=\"shutdownDaemon();\" onkeydown=\"return keyud(event,1);\" onkeyup=\"return keyud(event,0);\" onkeypress=\"return sendkey(event);\">");
	break;
	case TAB_FILES:
		if(func==FUNC_VIEW)
			printf("</head><body>");
		else
			printf("</head><body onload=\"initCatchDrop('%s');\">",homedir);
	break;
	default:
		printf("</head><body>");
	break;
	}

#if USELOGIN
	if(login)
		allowtab[TAB_LOGIN]=0;
	else
		allowtab[TAB_LOGOUT]=0;
#endif

	for(i=0;i<TAB_NUMTABS;++i)
	{
		if(allowtab[i])
			printf("<div class=\"fl tab%d cctl cctr\">&nbsp;<a class=\"tabl%d\" href=\"" NAME "?tab=%d\">%s</a>&nbsp;</div>",tabnum==i?1:0,tabnum==i?1:0,i,tabnames[i]);
	}
	printf("<br class=\"cb\">");
	printf("<div class=\"box cctr ccbl ccbr\">");
	switch(tabnum)
	{
#if USELOGIN
	case TAB_LOGIN:
		if(secure==false)
		{
			printf("If this computer is accessable from the Internet then I strongly suggest that you connect to this program using a secure connection. Just manually change the 'http' on the browsers URL bar to 'https'<br><br>");
		}

		printf("<form name=\"login\" id=\"login\" method=\"post\" action=\"" NAME "\">");
		printf("<input type=\"hidden\" name=\"func\" value=\"" VALSTRINGIFY(FUNC_LOGIN) "\">");
		printf("<table cellpadding=\"0\" cellspacing=\"0\">");
		printf("<tr><td>Login As</td><td><input id=\"name\" name=\"name\" type=\"text\"></td></tr>");
		printf("<tr><td>Password</td><td><input id=\"password\" name=\"password\" type=\"password\"></td></tr>");
		printf("<tr><td>Stay Logged in&nbsp;</td><td><input type=\"checkbox\" name=\"stay\" value=\"\"></td></tr>");
		printf("</table>");
		printf("<input class=\"ebutton\" type=\"submit\" name=\"slogin\" value=\"Login\">");
		printf("</form>");
	break;
	case TAB_LOGOUT:
		printf("<form name=\"logout\" id=\"logout\" method=\"post\" action=\"" NAME "\">");
		printf("<input type=\"hidden\" name=\"func\" value=\"" VALSTRINGIFY(FUNC_LOGOUT) "\">");
		printf("<input class=\"ebutton\" type=\"submit\" name=\"slogout\" value=\"Logout\">");
		printf("</form>");
	break;
#endif
	case TAB_SYSTEM:
	{
		struct sysinfo si;
		struct utsname name;
		time_t time_of_day;
		struct tm *tinfo;

		time_of_day = time( NULL );
		tinfo=localtime( &time_of_day );

		printf("Current date and time is: %s<br>",myasctime(tinfo));

		if (uname(&name) == -1)
			printf("System: Cannot get system name<br>");
		else
		{
			printf("System Name: %s<br>",name.sysname);
			printf("Node Name: %s<br>",name.nodename);
			printf("Release: %s<br>",name.release);
			printf("Version: %s<br>",name.version);
			printf("Machine: %s<br>",name.machine);
		}

		if(sysinfo(&si)!=0)
		{
			printf("System Info: Error getting info!<br>");
		}
		else
		{
			unsigned long ssb;
			int u;

			printf("Time Since Boot: ");
			ssb=si.uptime;
			u=ssb/(60*60*24);
			if(u)
			{
				//days
				if(u==1)
					printf("1 Day,");
				else
					printf("%d Days,",u);
				ssb-=(u*60*60*24);
			}
			u=ssb/(60*60);
			if(u)
			{
				//hours
				if(u==1)
					printf("1 Hour,");
				else
					printf("%d Hours,",u);
				ssb-=(u*60*60);
			}
			u=ssb/60;
			if(u)
			{
				//minutes
				if(u==1)
					printf("1 Minute,");
				else
					printf("%d Minutes,",u);
				ssb-=u*60;
			}
			u=ssb;
			if(u==1)
				printf("1 Second<br>");
			else
				printf("%d Seconds<br>",u);

			printf("Total Ram: %ldk<br>",si.totalram/1024);
			printf("Free Ram: %ldk<br>",si.freeram/1024);
			printf("Shared Ram: %ldk<br>",si.sharedram/1024);
			printf("Buffered Ram: %ldk<br>",si.bufferram/1024);
			printf("Total Swap: %ldk<br>",si.totalswap/1024);
			printf("Free Swap: %ldk<br>",si.freeswap/1024);
			printf("Total High Memory: %ldk<br>",si.totalhigh/1024);
			printf("Free High Memory: %ldk<br>",si.freehigh/1024);
			printf("Memory Unit size in Bytes: %d<br>",si.mem_unit);
			printf("Current # Processes: %d<br>",si.procs);
		}
	}
	break;
	case TAB_USERS:
	{
		struct passwd *pwe;

		printf("<table cellpadding=\"0\" cellspacing=\"0\">");
		printf("<tr class=\"b\">");
		printf("<td>Name&nbsp;</td>");
		printf("<td>UID&nbsp;</td>");
		printf("<td>GID&nbsp;</td>");
		printf("<td>Home Directory&nbsp;</td>");
		printf("<td>Shell&nbsp;</td>");
		printf("</tr>");
		do
		{
			pwe=getpwent();
			if(!pwe)
				break;
			printf("<tr><td>%s&nbsp;</td><td>%d&nbsp;</td><td>%d&nbsp;</td><td>%s&nbsp;</td><td>%s&nbsp;</td></tr>",pwe->pw_name,pwe->pw_uid,pwe->pw_gid,pwe->pw_dir,pwe->pw_shell);
		}while(1);
		printf("</table>");
	}
	break;
	case TAB_MOUNTS:
	{
		FILE *mountTable;
		struct mntent *mountEntry;

		printf("<table cellpadding=\"0\" cellspacing=\"0\">");
		printf("<tr class=\"b\">");
		printf("<td>FileSystem&nbsp;</td>");
		printf("<td>1k-blocks&nbsp;</td>");
		printf("<td>Used&nbsp;</td>");
		printf("<td>Available&nbsp;</td>");
		printf("<td>Use%%&nbsp;</td>");
		printf("<td>Mounted on&nbsp;</td>");
		printf("</tr>");

		mountTable = setmntent(mtab_file, "r");
		if (mountTable == 0)
		{
			perror(mtab_file);
		}

		while ((mountEntry = getmntent(mountTable)))
		{
			df(mountEntry->mnt_fsname, mountEntry->mnt_dir);
		}
		endmntent(mountTable);
		printf("</table>");
	}
	break;
	case TAB_CPU:
		//show cpuinfo
		fp=fopen("/proc/cpuinfo","rb");
		if(!fp)
			printf("Error unable to get info<br>");
		else
			printfile(fp);
	break;
	case TAB_DEVICES:
		//show devices
		fp=fopen("/proc/devices","rb");
		if(!fp)
			printf("Error unable to get info<br>");
		else
			printfile(fp);
	break;
	case TAB_FILESYSTEMS:
		//show filesystems
		fp=fopen("/proc/filesystems","rb");
		if(!fp)
			printf("Error unable to get info<br>");
		else
			printfile(fp);
	break;
	case TAB_INTERFACES:
	{
		/* show interface addresses */
	   struct ifaddrs *ifaddr, *ifa;
	   int family, s;
	   char host[NI_MAXHOST];

	   if (getifaddrs(&ifaddr) == -1) 
			printf("Error unable to get info<br>");
	   else
	   {
	       /* Walk through linked list, maintaining head pointer so we can free list later */
	       for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next)
		   {
	           family = ifa->ifa_addr->sa_family;

	           /* Display interface name and family (including symbolic
	              form of the latter for the common families) */

	           printf("%s  address family: %d%s",
	                   ifa->ifa_name, family,
	                   (family == AF_PACKET) ? " (AF_PACKET)" :
	                   (family == AF_INET) ?   " (AF_INET)" :
	                   (family == AF_INET6) ?  " (AF_INET6)" : "");

	           /* For an AF_INET* interface address, display the address */

	           if (family == AF_INET || family == AF_INET6)
			   {
	               s = getnameinfo(ifa->ifa_addr,(family == AF_INET) ? sizeof(struct sockaddr_in) :
	                                             sizeof(struct sockaddr_in6),
	                       host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
	               if (s != 0)
	                   printf(" getnameinfo() failed: %s", gai_strerror(s));
	               else
					   printf(" address: <%s>", host);
	           }
			   printf("<br>");
	       }

	       freeifaddrs(ifaddr);
	   }
	}
	break;
	case TAB_PORTS:
		fp=fopen("/proc/net/tcp","rb");
		if(!fp)
			printf("Error unable to get info<br>");
		else
			printfile(fp);

		fp=fopen("/proc/net/udp","rb");
		if(!fp)
			printf("Error unable to get info<br>");
		else
			printfile(fp);

		fp=fopen("/proc/net/raw","rb");
		if(!fp)
			printf("Error unable to get info<br>");
		else
			printfile(fp);
	break;
	case TAB_PROCESSES:
	{
		ReadDir dir;
		ReadEntry *re;
		char filename[8192];	/* worst case, a really LONG name */

		/* print all processes running */
		if(dir.Open("/proc")==false)
		{
			printf("Error unable to get proc info<br>");
		}
		else
		{
			while(1)
			{
				re=dir.GetEntry();
				if(!re)
					break;

				if(re->m_shortname[0]>='0' && re->m_shortname[0]<='9')
				{
					if(stat(re->m_fullname,&statbuf)!=-1)
					{
						if(S_ISDIR(statbuf.st_mode))
						{
							//ok read and display the cmdline for this process
							sprintf(filename,"%s/cmdline",re->m_fullname);
							printf("%s-",re->m_shortname);
							fp=fopen(filename,"rb");
							if(fp)
							{
								while(1)
								{
									unsigned char c=fgetc(fp);
									if(feof(fp))
										break;
									if(c==0)
										printf(" ");
									else
										printf("%c",c);
								}
								fclose(fp);
							}
							//get stat
							sprintf(filename,"%s/stat",re->m_fullname);
							fp=fopen(filename,"rb");
							if(fp)
							{
								int area=0;
								while(1)
								{
									unsigned char c=fgetc(fp);
									if(feof(fp))
										break;

									if(c==0 || c==' ')
										++area;
									else
									{
										if(area==1)
											printf("%c",c);
									}
								}
								fclose(fp);
							}
							printf("<br>");
						}
					}
				}
			}
		}
	}
	break;
	case TAB_WEB:
		for(i=0;i<(int)(sizeof(ev)/sizeof(char *));++i)
		{
			cp=getenv(ev[i]);
			if(cp)
				printf("%s='%s'<br>",ev[i],cp);
		}
	break;
	case TAB_CREDITS:
		printf("<span class=\"walrus\"></span><span class=\"big\">" NAME " " VERSION "</span><br>");
		printf("<span class=\"b\">Programmed by Kevin Pickell (kevin@scale18.com) December 2009</span><br><br>");
		printf("<br>");
		printf("<a href=\"http://www.fsf.org/\"><img class=\"nb\" alt=\"FSF Logo\" src=\"http://www.gnu.org/graphics/logo-fsf.org-tiny.png\"></a><br>");
		printf("<span class=\"b\">wpanel</span> is free software; you can redistribute it and/or modify<br>");
		printf("it under the terms of the GNU General Public License as published by<br>");
		printf("the Free Software Foundation; version 2.<br>");
		printf(NAME " is distributed in the hope that it will be useful,<br>");
		printf("but WITHOUT ANY WARRANTY; without even the implied warranty of<br>");
		printf("MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the<br>");
		printf("GNU General Public License for more details.<br><br>");
		printf("<a class=\"klink\" href=\"http://www.gnu.org/licenses/gpl-2.0.txt\">http://www.gnu.org/licenses/gpl-2.0.txt</a><br><br>");
	break;
	case TAB_FILES:
		if(func==FUNC_VIEW)
		{
			unsigned int imgtype;

			/* show file */
			printf("<span class=\"b\">Current File:'%s'</span><br>",viewfn);

			/* is this an image? */
			imgtype=getImageType(viewfn);
			if(imgtype!=IMAGE_UNKNOWN)
			{
				/* yes it is an image */
				printf("<img id=\"image\" src=\"?func=" VALSTRINGIFY(FUNC_GET) "&amp;fn=%s\">",viewfn);
			}
			else
			{
				fp=fopen(viewfn,"rb");
				if(!fp)
					printf("Error unable to open file<br>");
				else
				{
					printf("<input class=\"ebutton\" type=\"button\" name=\"edit\" value=\"Edit\" onClick=\"doEdit()\">");	//hooked to javascript to enable edit/save
					printf("&nbsp;&nbsp;");
					printf("<input class=\"dbutton\" id=\"savebutton\" type=\"submit\" name=\"save\" value=\"Save\" disabled=\"disabled\"  onClick=\"doSave('%s')\">",viewfn);	//save changes, are you sure?
					printf("<span id=\"status\" class=\"done\"></span>");
					printf("<span id=\"results\" class=\"b\"></span>");
					printf("<br class=\"cb\">");
					printf("<textarea id=\"editfile\" type=\"text\" disabled=\"disabled\">");
					while(1)
					{
						unsigned char c=fgetc(fp);
						if(feof(fp))
							break;
						switch(c)
						{
						case '<':
							printf("&lt;");
						break;
						case '>':
							printf("&gt;");
						break;
						case '&':
							printf("&amp;");
						break;
						default:
							printf("%c",c);
						break;
						}
					};
					fclose(fp);
					printf("</textarea>");
				}
			}
		}
		else
		{
			ReadDir dir;
			ReadEntry *re;
			list<string> de;
			list<string>::iterator it;

			//dropped files will appear in the droplist
			printf("<div id=\"droparea\" class=\"b cctl cctr ccbl ccbr\">Current Directory:'%s'<br>",homedir);
			printf("Drag and Drop files into this area to upload (Currently only works in FF3.6 Beta5+)<br>");
			printf("<div id=\"droplist\"></div>");
			printf("</div>");

			if(dir.Open(homedir)==false)
			{
				printf("Error unable to open directory<br>");
			}
			else
			{
				printf("<table cellpadding=\"0\" cellspacing=\"0\">");

				//add ".." for previous (parent) folder
				if(strlen(homedir)>1)
				{
					unsigned int ff;
					unsigned int newend=0;
					bool grab=true;
					string prevdir;

					prevdir=homedir;

					/* remove last folder found */
					for(ff=0;ff<prevdir.length();++ff)
					{
						if(prevdir[ff]==dirc[0])
							grab=true;
						else if(grab)
						{
							newend=ff;
							grab=false;
						}
					}
					prevdir=prevdir.substr(0,newend);

					printf("<tr><td><a class=\"klink\" href=\"" NAME "?func=" VALSTRINGIFY(FUNC_BROWSE) "&amp;dir=%s\"><span class=\"folder\"></span>..</a></td><td></td></tr>",prevdir.c_str());
				}

				while(1)
				{
					re=dir.GetEntry();
					if(!re)
						break;

					if(re->m_shortname[0]!='.')
						de.push_back(re->m_shortname);
				}
				dir.Close();

				de.sort(compare_nocase);

				for (it=de.begin(); it!=de.end(); ++it)
				{
					re=dir.GetFullName(it->c_str());
					if(stat(re->m_fullname,&statbuf)!=-1)
					{
						if(S_ISDIR(statbuf.st_mode))
							printf("<tr><td><a class=\"klink\" href=\"" NAME "?func=" VALSTRINGIFY(FUNC_BROWSE) "&amp;dir=%s/\"><span class=\"folder\"></span>%s</a></td><td></td><td></td><td></td></tr>",re->m_fullname,re->m_shortname);
						else
							printf("<tr><td><a class=\"klink\" href=\"" NAME "?func=" VALSTRINGIFY(FUNC_VIEW) "&amp;fn=%s\"><span class=\"file\"></span>%s</a></td><td>&nbsp;%ld</td><td>&nbsp;<input class=\"ebutton\" type=\"button\" name=\"rename\" value=\"Rename File\" onClick=\"askRenameFile('%s')\">&nbsp;</td><td>&nbsp;<input class=\"ebutton\" type=\"button\" name=\"delete\" value=\"Delete File\" onClick=\"askDeleteFile('%s')\">&nbsp;</td></tr>",re->m_fullname,re->m_shortname,statbuf.st_size,re->m_fullname,re->m_fullname);
					}
					else
					{
						/* cannot stat file */
						printf("<tr><td class=\"klink\"><span class=\"file\"></span>%s</td><td>&nbsp;Error: Cannot get file information error=",re->m_shortname);
						switch(errno)
						{
						case EACCES:
							printf("EACCES");
						break;
						case EIO:
							printf("EIO");
						break;
						case ELOOP:
							printf("ELOOP");
						break;
						case ENAMETOOLONG:
							printf("ENAMETOOLONG");
						break;
						case ENOENT:
							printf("ENOENT");
						break;
						case ENOTDIR:
							printf("ENOTDIR");
						break;
						case EOVERFLOW:
							printf("EOVERFLOW");
						break;
						default:
							printf("UNKNOWN");
						break;
						}
						printf("</td><td></td><td></td></tr>");
					}
				}
				printf("</table>");
			}
		}
	break;
	case TAB_SHELL:
		printf("<span id=\"status\" class=\"done\"></span>");
		printf("<input class=\"ebutton\" type=\"button\" id=\"paste\" name=\"paste\" value=\"Paste\" onClick=\"doPaste()\">");
		printf("<br>");
		printf("<div id=\"output\" type=\"text\"></div>");
	break;
#if DOTEST
	case TAB_TEST:
		/* show all characters available in monospace font */
		printf("<span class=\"mono\">");
		for(i=1;i<65536;++i)
			printf("'&#%d;' %d 0x%04x<br>",i,i,i);
		printf("</span>");
	break;
#endif
	}

	printf("</div></body></html>\n");
	return(0);
}

class TtyStyle
{
public:
	TtyStyle() {Reset(true);}
	void Reset(bool clearall=true);
	void SetStyle(unsigned int style) {m_style=style;}
	void SetStyleBit(unsigned int bit) {m_style|=bit;}
	void ClearStyleBit(unsigned int bit) {m_style&=~bit;}
	void SetFGColor(unsigned int color) {m_fgcolor=color;}
	void SetBGColor(unsigned int color) {m_bgcolor=color;}
	unsigned int GetStyle(void) {return m_style;}
	unsigned int GetFGColor(void) {return m_fgcolor;}
	unsigned int GetBGColor(void) {return m_bgcolor;}
	/* GetStyleBits is what is sent from the cgi-bin to the browser, it is the style bits along with both the
	   forground and background color bits all packed into 1 value */
	unsigned int GetStyleBits(void) {return m_style|(m_fgcolor<<(STYLE_BITSUSED))|(m_bgcolor<<(STYLE_BITSUSED+STYLE_COLORBITS));}
private:
	unsigned int m_style:STYLE_BITSUSED;
	unsigned int m_fgcolor:STYLE_COLORBITS;
	unsigned int m_bgcolor:STYLE_COLORBITS;
};

void TtyStyle::Reset(bool clearall)
{
	m_fgcolor=7;
	m_bgcolor=0;

	if(clearall)
		m_style=0;
	else
		m_style&=(STYLE_DOUBLETOP|STYLE_DOUBLEBOTTOM);	/* clear all bits except these */
}

class TtyChar
{
public:
	TtyChar() {m_c=0;}
	void Reset(void) {m_c=0;m_style.Reset();}
	void Set(unsigned int c,TtyStyle *style);
	unsigned int GetCharCode(void) {return m_c;}
	unsigned int GetStyleBits(void) {return m_style.GetStyleBits();}
private:
	unsigned int m_c;
	TtyStyle m_style;
};

void TtyChar::Set(unsigned int c,TtyStyle *style)
{
	m_c=c;
	m_style=*(style);
}

class TtyLine
{
public:
	TtyLine(int slot) {m_slot=slot;m_time=1;m_slottime=1;m_numcols=0;m_char=0;}
	~TtyLine() {if(m_char) delete []m_char;}
	void SetNumCols(int numcols) {m_numcols=numcols;m_char=new TtyChar[numcols];}
	void Clear(unsigned int time);
	void SetChar(unsigned int time,int col,unsigned int c,TtyStyle *style);
	void InsertChar(int col);
	void DeleteChar(int col);
	TtyChar *GetChar(int col) {return &m_char[col];}
	void SetTime(unsigned int t) {m_time=t;}
	unsigned int GetTime(void) {return m_time;}
	int GetSlot(void) {return m_slot;}
	void SetSlotTime(unsigned int t) {m_slottime=t;}
	unsigned int GetSlotTime(void) {return m_slottime;}
private:
	int m_slot;
	unsigned int m_time;
	unsigned int m_slottime;
	int m_numcols;
	TtyChar *m_char;
};

void TtyLine::Clear(unsigned int time)
{
	int i;
	TtyStyle defstyle;

	m_time=time;
	for(i=0;i<m_numcols;++i)
		m_char[i].Set(i==0?' ':0,&defstyle);
}

void TtyLine::SetChar(unsigned int time,int col,unsigned int c,TtyStyle *style)
{
	m_time=time;
	m_char[col].Set(c,style);
}

void TtyLine::InsertChar(int col)
{
	int c;

	c=m_numcols-1;
	while(c>=col)
	{
		m_char[c+1]=m_char[c];
		--c;
	}
}

void TtyLine::DeleteChar(int col)
{
	int c;

	c=col;
	while((c+1)<m_numcols)
	{
		m_char[c]=m_char[c+1];
		++c;
	}
	m_char[m_numcols-1].Reset();
}

#define MAXARGS 32

enum CHARSET
{
CHARSET_NORMAL,
CHARSET_PC,
CHARSET_VT100GRAPHICS};

class Tty
{
public:
	Tty(int numlines,int numshowlines,int numcols);
	~Tty();
	void Reset(void);
	void Print(unsigned char c);
	int GetNumLines(void) {return m_numlines;}
	int GetNumColumns(void) {return m_numcols;}
	TtyLine *GetLine(int line) {return m_line[line];}
	unsigned int GetTime(void) {return m_time;}
	void SetTime(unsigned int t);
	bool GetChanged(void) {return m_changed;}
	void SetChanged(bool c) {m_changed=c;}
	unsigned int GetTopLine(void) {return m_topline;}
	int GetCursorLineIndex() {return GetLineIndex(m_cursory);}
	void CheckCursor(void);
	void CheckCursorMoved(void);
	bool GetShowCursor(void) {return m_showcursor;}
	int GetCursorX(void) {return m_cursorx;}
	int GetCursorY(void) {return m_cursory;}
	void SwapLines(int l1,int l2);
	bool GetReverse(void) {return m_reverse;}
private:
	int GetLineIndex(int y);
	void PutAtCursor(unsigned int c);
	void CursorDown(int n);
	void CursorLeft(int n);
	void CursorRight(int n);
	int ParseArgs(const unsigned char *pa,int maxargs);
	void SetCursorX(int newx);
	void SetCursorY(int newy);
	void Clear(int topy,int lx,int bottomy,int rx,unsigned char c=0);
	/* member variables */
	unsigned int m_time;
	int m_numlines;
	int m_numcols;
	int m_topline;			/* scroll value to loop through lines, 0 to numlines-1 */
	int m_cursorx;			/* 0 to numcols-1 */
	int m_numshowlines;		/* number of lines visible */
	int m_historylines;		/* lines scrolled off of the top, 0 to numlines-showlines */
	int m_cursory;			/* on screen, 0 to showlines-1 */
	TtyStyle m_style;		/* current style */
	int m_args[MAXARGS];
	bool m_changed:1;
	bool m_autocr:1;
	bool m_inesc:1;	/* in escape mode */
	bool m_wrap:1;
	bool m_cursorwrap:1;
	bool m_reverse:1;
	unsigned char m_escbuf[128];
	int m_escbuflen;
	TtyLine **m_line;
	TtyLine *m_pcursorline;
	bool m_prevshowcursor;
	int m_prevcursorx;
	int m_scrolltop;
	int m_scrollbottom;
	int m_insertchars;
	bool m_showcursor;
	int m_savecursorx;
	int m_savecursory;
	int m_curset;
	CHARSET m_charsets[2];
};

Tty::Tty(int numlines,int numshowlines,int numcols)
{
	int i;

	m_changed=false;
	m_autocr=false;
	m_time=1;
	m_numlines=numlines;
	m_numshowlines=numshowlines;
	m_numcols=numcols;
	m_topline=0;
	m_cursorx=0;
	m_cursory=0;
	m_scrolltop=0;
	m_scrollbottom=numshowlines-1;
	m_cursorwrap=false;
	m_historylines=0;
	m_inesc=false;
	m_line=new TtyLine *[numlines];
	m_pcursorline=0;
	m_prevcursorx=0;
	m_insertchars=0;
	m_wrap=false;
	m_showcursor=true;
	m_reverse=false;
	m_prevshowcursor=true;
	m_curset=0;
	m_charsets[0]=CHARSET_NORMAL;
	m_charsets[1]=CHARSET_VT100GRAPHICS;
	for(i=0;i<numlines;++i)
	{
		m_line[i]=new TtyLine(i);
		m_line[i]->SetNumCols(numcols);
	}
}

void Tty::Reset(void)
{
	int i;

	m_curset=0;
	m_autocr=false;
	m_cursorx=0;
	m_cursory=0;
	m_cursorwrap=false;
	m_showcursor=true;
	m_scrolltop=0;
	m_scrollbottom=m_numshowlines-1;
	m_insertchars=0;
	m_wrap=false;
	m_reverse=false;
	m_pcursorline=0;
	m_prevcursorx=0;
	m_prevshowcursor=true;
	m_topline=0;
	m_historylines=0;
	m_curset=0;
	m_charsets[0]=CHARSET_NORMAL;
	m_charsets[1]=CHARSET_VT100GRAPHICS;
	for(i=0;i<m_numlines;++i)
	{
		if(m_line[i]->GetChar(0)->GetCharCode()!=0)
			m_line[i]->Clear(m_time+1);
	}
	m_changed=true;
}

void Tty::SetTime(unsigned int t)
{
	int i;

	/* handle wrapping time */
	if(t==MAXTIME)
	{
		/* reset all line change times to 0 and current time to 1 to force a complete re-send! */
		dlog("Time has wrapped, resetting all last updated lines to zero!\n");
		for(i=0;i<m_numlines;++i)
		{
			m_line[i]->SetTime(0);
			m_line[i]->SetSlotTime(0);
		}
		t=1;
	}
	m_time=t;
}

void Tty::CheckCursor(void)
{
	TtyLine *cursorline=m_line[GetCursorLineIndex()];
	TtyChar *ttyc;
	int x;
	unsigned char c;
	TtyStyle defstyle;

	//change any nulls to spaces so cursor will appear!
	for(x=0;x<=m_cursorx;++x)
	{
		ttyc=cursorline->GetChar(x);
		c=ttyc->GetCharCode();
		if(!c)
			ttyc->Set(' ',&defstyle);
	}
}

void Tty::CheckCursorMoved(void)
{
	TtyLine *cursorline=m_line[GetCursorLineIndex()];

	//check to see if cursor moved since last update
	if((m_pcursorline!=cursorline) || (m_prevcursorx!=m_cursorx) || (m_prevshowcursor!=m_showcursor))
	{
		//flag as changed
		m_changed=true;
		if(m_pcursorline)
			m_pcursorline->SetTime(m_time+1);
		cursorline->SetTime(m_time+1);
		m_pcursorline=cursorline;
		m_prevcursorx=m_cursorx;
		m_prevshowcursor=m_showcursor;
		CheckCursor();
		//dlog("cursor has moved!\n");
	}
}

/* fill in m_args values and return number of args found */
/* arg format is 'nnnn;nnn;nnn' where n is a digit */

int Tty::ParseArgs(const unsigned char *pa,int maxargs)
{
	int index=0;
	int val=0;
	bool gotd=false;
	bool neg=false;
	char c;

	/* parse the args into m_args */
	do
	{
		c=*(pa++);
		if((c=='-') && gotd==false && neg==false)
			neg=true;
		else if((c>='0') && (c<='9'))
		{
			gotd=true;
			val=(val*10)+(c-'0');
		}
		else
		{
			if(gotd)
			{
				/* check for overflow */
				if(index==maxargs)
					return(index);
				/* store value */
				if(neg)
					m_args[index]=-val;
				else
					m_args[index]=val;
				neg=false;
				gotd=false;
				val=0;
				++index;
			}
			if(c!=';')
				return(index);
		}
	}while(1);
	/* never gets here */
	return(-1);
}

void Tty::SwapLines(int l1,int l2)
{
	TtyLine *pl1=m_line[l1];
	TtyLine *pl2=m_line[l2];

	m_line[l2]=pl1;
	m_line[l1]=pl2;

	//set slot changed time so this is updated
	m_changed=true;
	pl1->SetSlotTime(m_time+1);
	pl2->SetSlotTime(m_time+1);
}

void Tty::Clear(int topy,int lx,int bottomy,int rx,unsigned char c)
{
	int y;
	int x;
	int line;
	TtyStyle defstyle;

	m_changed=true;
	for(y=topy;y<bottomy;++y)
	{
		line=GetLineIndex(y);
		for(x=lx;x<rx;++x)
			m_line[line]->SetChar(m_time+1,x,c!=0?c:(x==0?' ':0),&defstyle);
	}
}

void Tty::SetCursorX(int newx)
{
	if(newx<0)
		newx=0;
	m_cursorx=newx;
	CheckCursor();
}

void Tty::SetCursorY(int newy)
{
	m_cursorwrap=false;
	if(newy<0)
		newy=0;
	else if(newy>=m_numshowlines)
		newy=m_numshowlines-1;
	m_cursory=newy;

	CheckCursor();
}

#define ESC 27

/* print the character to the tty */
void Tty::Print(unsigned char c)
{
	int i;

	/* are we in escape mode? */
	if(m_inesc)
	{
		//dlog("m_escbuflen=%d\n",m_escbuflen);
		/* check to see if room to store this in the buffer (leave room for the null terminator) */
		if((m_escbuflen+1)==sizeof(m_escbuf))
		{
			/* error, we cannot fit any more characters into the escape buffer */
			/* obviously this is some sort of escape sequence that we don't understand */
			dlog("unknown esc sequence '");
			for(i=0;i<m_escbuflen;++i)
				dlog("%c",m_escbuf[i]);
			dlog("%c'\r\n",c);

			PutAtCursor(ESC);
			for(i=0;i<m_escbuflen;++i)
				PutAtCursor(m_escbuf[i]);
			PutAtCursor(c);
			m_inesc=false;
		}
		else if(c==ESC)
		{
			/* obviously this is some sort of escape sequence that we don't understand */
			dlog("unknown esc sequence '");
			for(i=0;i<m_escbuflen;++i)
				dlog("%c",m_escbuf[i]);
			dlog("'\r\n");

			PutAtCursor(ESC);
			for(i=0;i<m_escbuflen;++i)
				PutAtCursor(m_escbuf[i]);
			m_escbuflen=0;
		}
		else
		{
			/* is this a complete sequence or still in progress?? */
			if(!m_escbuflen)
			{
				switch(c)
				{
				case '7':
					/* save */
					m_savecursorx=m_cursorx;
					m_savecursory=m_cursory;
					m_inesc=false;	//esc is done!
					return;
				break;
				case '8':
					/* restore */
					m_cursorx=m_savecursorx;
					m_cursory=m_savecursory;
					m_cursorwrap=false;
					m_inesc=false;	//esc is done!
					return;
				break;
				case 'c':
					/* reset! */
					Reset();
					m_inesc=false;	//esc is done!
					return;
				break;
				case 'H':
					/* Set tab stop at current column */
					/* not supported yet */
					m_inesc=false;	//esc is done!
					return;
				break;
				case 'M':
					/* reverse linefeed */
					SetCursorX(0);
					if(m_cursory==m_scrolltop)
					{
						int y=m_scrollbottom-1;
						while(y>=m_cursory)
						{
							SwapLines(GetLineIndex(y),GetLineIndex(y+1));
							--y;
						}
						GetLine(GetLineIndex(m_cursory))->Clear(m_time+1);
					}
					else
						SetCursorY(m_cursory-1);
					m_inesc=false;	//esc is done!
					return;
				case 'Z':
					printf("%c[?6c",27);
					m_inesc=false;	//esc is done!
					return;
				break;
				case '=':
					/* select alternate keypad */
					m_inesc=false;	//esc is done!
					return;
				break;
				case '>':
					/* select numeric keypad */
					m_inesc=false;	//esc is done!
					return;
				break;
				case '<':
					/* select ANSI mode */
					m_inesc=false;	//esc is done!
					return;
				break;
				}
			}
			else
			{
				int numargs;

				if(m_escbuf[0]==']' && c==7)
				{
					/* operating system command */
					//ESC ] 0 ; txt BEL	Set icon name and window title to txt.
					//ESC ] 1 ; txt BEL	Set icon name to txt.
					//ESC ] 2 ; txt BEL	Set window title to txt.
					//ESC ] 4 6 ; name BEL	Change log file to name (normally disabled 	by a compile-time option)
					//ESC ] 5 0 ; fn BEL	Set font to fn.
					m_inesc=false;	//esc is done!
					return;
				}
				else if(m_escbuf[0]=='(')
				{
					switch(c)
					{
					case '0':
						m_charsets[0]=CHARSET_VT100GRAPHICS;
					break;
					case 'B':
						m_charsets[0]=CHARSET_NORMAL;
					break;
					case 'U':
						m_charsets[0]=CHARSET_PC;
					break;
					default:
						/* huh?? */
					break;
					}
					m_inesc=false;	//esc is done!
					return;
				}
				else if(m_escbuf[0]==')')
				{
					switch(c)
					{
					case '0':
						m_charsets[1]=CHARSET_VT100GRAPHICS;
					break;
					case 'B':
						m_charsets[1]=CHARSET_NORMAL;
					break;
					case 'U':
						m_charsets[1]=CHARSET_PC;
					break;
					default:
						/* huh?? */
					break;
					}
					m_inesc=false;	//esc is done!
					return;
				}
				else if(m_escbuf[0]=='#')
				{
					switch(c)
					{
					case '3':
						/* Double size top line */
		                m_style.SetStyleBit(STYLE_DOUBLETOP);
		                m_style.ClearStyleBit(STYLE_DOUBLEBOTTOM);
						m_inesc=false;	//esc is done!
						return;
					break;
					case '4':
						/* Double size bottom line */
		                m_style.ClearStyleBit(STYLE_DOUBLETOP);
		                m_style.SetStyleBit(STYLE_DOUBLEBOTTOM);
						m_inesc=false;	//esc is done!
						return;
					break;
					case '1':
					case '2':
					case '5':
					case '6':
						/* mixed single/double width and height modes not supported */
						m_inesc=false;	//esc is done!
						return;
					break;
					}
				}
				else if(m_escbuf[0]=='[')
				{
					/* [nnn;nnn;nnnL where n=0-9 are numeric arguments and L=command letter code */
					switch(c)
					{
					case '@':
						/* insert the next n characters at the cursor position */
						switch(ParseArgs(m_escbuf+1,1))
						{
						case 0:
							m_insertchars=1;
						break;
						case 1:
							m_insertchars=m_args[0];
						break;
						}
						m_inesc=false;	//esc is done!
						return;
					break;
					case 'A':
					{
						int n=0;
						/* cursor up */
						switch(ParseArgs(m_escbuf+1,1))
						{
						case 0:
							n=1;
						break;
						case 1:
							n=m_args[0];
						break;
						}
						if(n)
							SetCursorY(m_cursory-n);
						m_inesc=false;	//esc is done!
						return;
					}
					break;
					case 'B':
						numargs=ParseArgs(m_escbuf+1,1);

						//cursor down number of characters specificed!
						CursorDown(numargs>0?m_args[0]:1);
						m_inesc=false;	//esc is done!
						return;
					break;
					case 'C':
						numargs=ParseArgs(m_escbuf+1,1);

						//cursor right number of characters specificed!
						CursorRight(numargs>0?m_args[0]:1);
						m_inesc=false;	//esc is done!
						return;
					break;
					case 'c':
						if(m_escbuf[1]=='?')
						{
							numargs=ParseArgs(m_escbuf+2,1);

							if(numargs==0 || (numargs==1 && m_args[0]==0))
								printf("%c[?6c",27);
							m_inesc=false;
							return;
						}
					break;
					case 'd':
						switch(ParseArgs(m_escbuf+1,1))
						{
						case 1:
							SetCursorY(m_args[0]-1);
						break;
						default:
							/* error */
						break;
						}
						m_inesc=false;
						return;
					break;
					case 'D':
						numargs=ParseArgs(m_escbuf+1,1);

						//cursor left number of characters specificed!
						CursorLeft(numargs>0?m_args[0]:1);
						m_inesc=false;	//esc is done!
						return;
					break;
					case 'g':
						/* clear tab stops */
						/* we don't support tab stops yet */
						/* no number is at current cursor */
						/* 3=all */
						m_inesc=false;
						return;
					break;
					case 'G':
						switch(ParseArgs(m_escbuf+1,1))
						{
						case 1:
							SetCursorX(m_args[0]-1);
						break;
						default:
							/* error */
						break;
						}
						m_inesc=false;
						return;
					break;
					case 'h':
						if(m_escbuf[1]=='?')
						{
							if(ParseArgs(m_escbuf+2,1)==1)
							{
								/* set */
								switch(m_args[0])
								{
								case 1: /* cursor key=Application */
								case 2: /* Term=ANSI */
								case 3: /* Columns=132 */
								case 4: /* Scrolling=Smooth */
								case 6: /* Origin=Relative */
								case 8: /* Key=Repeat */
								case 18: /* Print Form Feed=Yes */
								case 19: /* Print Extent=Full Screen */
										/* unsupported so far */
								break;
								case 5: /* Screen=Reverse */
									if(m_reverse==false)
									{
										m_reverse=true;
										for(i=0;i<NUMLINES;++i)
											m_line[i]->SetTime(m_time+1);

										m_changed=true;
									}
								break;
								case 7:
									/* wrap mode */
									m_wrap=true;
								break;
								case 25:
									m_showcursor=true;
								break;
								default:
									dlog("Unhandled mode set '%d'\n",m_args[0]);
								break;
								}
							}
							else
							{
								dlog("Unhandled mode set '%s'\n",m_escbuf);
							}
							m_inesc=false;
							return;
						}
						else
						{
							if(ParseArgs(m_escbuf+1,1)==1)
							{
								if(m_args[0]==20)
								{
									m_autocr=true;
								}
								else
								{
									/* error, unhandled mode */
								}
							}
							else
							{
								/* error */
							}
							m_inesc=false;
							return;
						}
					break;
				    case 'f':
					case 'H':	/* put cursor at row/col */
						switch(ParseArgs(m_escbuf+1,3))
						{
						case 0:
							SetCursorX(0);
							SetCursorY(0);
						break;
						case 1:
							SetCursorY(m_args[0]-1);
							SetCursorX(0);
						break;
						case 2:
							SetCursorY(m_args[0]-1);
							SetCursorX(m_args[1]-1);
						break;
						default:
							/* error bad format! */
						break;
						}
						m_inesc=false;
						return;
					break;
				    case 'J':
						if(ParseArgs(m_escbuf+1,1)==0)
							Clear(m_cursory,m_cursorx,m_numshowlines,m_numcols);
						else if(m_args[0]==1)
							Clear(0,0,m_cursory,m_cursorx);
						else
				            Clear(0, 0, m_numshowlines, m_numcols);
						m_inesc=false;
						return;
					break;
				    case 'K':
						if(ParseArgs(m_escbuf+1,1)==0)
							Clear(m_cursory,m_cursorx,m_cursory+1,m_numcols);	/* current line cursor->eol */
						else if(m_args[0]==1)
							Clear(m_cursory,0,m_cursory+1,m_cursorx);			/* current line beg to cursor */
						else
				            Clear(m_cursory, 0, m_cursory+1, m_numcols);		/* erase entire line */
						m_inesc=false;
						return;
					break;
					case 'l':
						if(m_escbuf[1]=='?')
						{
							if(ParseArgs(m_escbuf+2,1)==1)
							{
								/* clear */
								switch(m_args[0])
								{
								case 1: /* cursor key=Cursor */
								case 2: /* Term=VT52 */
								case 3: /* Columns=80 */
								case 4: /* Scrolling=Jump */
								case 6: /* Origin=Absolute */
								case 8: /* Key=No Repeat */
								case 18: /* Print Form Feed=No */
								case 19: /* Print Extent=Scrolling Region */
									/* unsupported so far */
								break;
								case 5: /* Screen=Normal */
									if(m_reverse==true)
									{
										m_reverse=false;
										for(i=0;i<NUMLINES;++i)
											m_line[i]->SetTime(m_time+1);
										m_changed=true;
									}
								break;
								case 7:
									/* wrap mode */
									m_wrap=false;
								break;
								case 25:
									m_showcursor=false;
								break;
								default:
									dlog("Unhandled mode clear '%d'\n",m_args[0]);
								break;
								}
							}
							else
							{
								dlog("Unhandled mode clear '%s'\n",m_escbuf);
							}
							m_inesc=false;
							return;
						}
						else
						{
							if(ParseArgs(m_escbuf+1,1)==1)
							{
								if(m_args[0]==3)
								{
									/* DECCRM mode - display control characters */
								}
								else if(m_args[0]==4)
								{
									/* DECIM mode - insert mode */
								}
								else if(m_args[0]==20)
								{
									m_autocr=false;
								}
								else
								{
									/* error, unhandled mode */
								}
							}
							else
							{
								/* error */
							}
							m_inesc=false;
							return;
						}
					break;
					case 'L':
					{
						/* insert row at cursor, clear new row here */
						int y;

						y=m_scrollbottom-1;
						while(y>=m_cursory)
						{
							SwapLines(GetLineIndex(y),GetLineIndex(y+1));
							--y;
						}
						GetLine(GetLineIndex(m_cursory))->Clear(m_time+1);
						m_inesc=false;
						return;
					}
					break;
					case 'm':
						numargs=ParseArgs(m_escbuf+1,MAXARGS);

						if(numargs<1 || m_escbuf[1]=='0')
						{
							/* reset all settings style settings */
			                m_style.Reset(false);
						}
						for(i=0;i<numargs;++i)
						{
				            switch(m_args[i])
				            {
				            case 0:
				                m_style.Reset(false);
			                break;
				            case 1:
				                m_style.SetStyleBit(STYLE_BOLD);
			                break;
				            case 4:
				                m_style.SetStyleBit(STYLE_UNDERLINE);
			                break;
				            case 5:
				                m_style.SetStyleBit(STYLE_BLINK);
			                break;
				            case 7:
				                m_style.SetStyleBit(STYLE_REVERSE);
			                break;
							case 10:
								m_charsets[m_curset]=CHARSET_NORMAL;
			                break;
							case 11:
								m_charsets[m_curset]=CHARSET_PC;
			                break;
							case 22:
				                m_style.ClearStyleBit(STYLE_BOLD);
			                break;
							case 24:
				                m_style.ClearStyleBit(STYLE_UNDERLINE);
			                break;
							case 25:
				                m_style.ClearStyleBit(STYLE_BLINK);
			                break;
							case 27:
				                m_style.ClearStyleBit(STYLE_REVERSE);
							break;
				            /* todo: 8 non-displayed */
				            case 30:
				            case 31:
				            case 32:
				            case 33:
				            case 34:
				            case 35:
				            case 36:
				            case 37:
								m_style.SetFGColor(m_args[i]-30);	/* 0 to 7 */
							break;
							case 39:
								/* default forground color (white) */
								m_style.SetFGColor(7);
							break;
				            case 40:
				            case 41:
				            case 42:
				            case 43:
				            case 44:
				            case 45:
				            case 46:
				            case 47:
								m_style.SetBGColor(m_args[i]-40);	/* 0 to 7 */
			                break;
							case 49:
								/* default background color (black) */
								m_style.SetBGColor(0);
							break;
							default:
								/* unknown */
								dlog("Unknown style value '%d'\n",m_args[i]);
							break;
				            }
				        }
						m_inesc=false;	//esc is done!
						return;
					break;
				    case 'M':
						/* delete n rows at cursor */
						if(ParseArgs(m_escbuf+1,1)==1)
						{
							do
							{
								int y=m_cursory;
								while((y+1)<m_numshowlines)
								{
									SwapLines(GetLineIndex(y),GetLineIndex(y+1));
									++y;
								}
								GetLine(GetLineIndex(m_numshowlines-1))->Clear(m_time+1);
    					    }while (--m_args[0]>0);
						}
						m_inesc=false;
						return;
					break;
					case 'P':
					{
						int n=0;

						/* delete n characters at the cursor position */
						switch(ParseArgs(m_escbuf+1,1))
						{
						case 0:
							n=1;
						break;
						case 1:
							n=m_args[0];
						break;
						}
						while(n)
						{
							m_line[GetCursorLineIndex()]->DeleteChar(m_cursorx);
							--n;
						}
						m_changed=true;
						m_line[GetCursorLineIndex()]->SetTime(m_time+1);
						m_inesc=false;	//esc is done!
						return;
					}
					break;
				    case 'r':
						/* set the scrolling screen region */
						/* so items don't scroll off the top into the history */
				        switch(ParseArgs(m_escbuf+1,2))
						{
						case 0:
							m_scrolltop=0;
							m_scrollbottom=m_numshowlines-1;
						break;
						case 2:
							m_scrolltop=m_args[0]-1;
							m_scrollbottom=m_args[1]-1;
						break;
						}
						m_inesc=false;
						return;
        			break;
					case 's':
						m_savecursorx=m_cursorx;
						m_savecursory=m_cursory;
						m_inesc=false;
						return;
					break;
					case 'u':
						m_cursorx=m_savecursorx;
						m_cursory=m_savecursory;
						m_cursorwrap=false;
						m_inesc=false;
						return;
					break;
					case 'X':
				        switch(ParseArgs(m_escbuf+1,1))
						{
						case 1:
						{
							int rx=m_cursorx+m_args[0];

							if(rx>m_numcols)
								rx=m_numcols;

							Clear(m_cursory,m_cursorx,m_cursory+1,rx,' ');
						}
						break;
						default:
							/* error */
						break;
						}
						m_inesc=false;
						return;
					}
				}
			}
			//dlog("buffering escape character %c at index %d\n",c,m_escbuflen);
			switch(c)
			{
			case 10:
			case 13:
					/* ignore any cr/lf inside the escape sequence */
			break;
			default:
				m_escbuf[m_escbuflen++]=c;
				m_escbuf[m_escbuflen]=0;
			break;
			}
		}
	}
	else
	{
		/* not in escape mode */
		if(c==ESC)
		{
			/* we are now in escape mode */
			m_inesc=true;
			m_escbuflen=0;
		}
		else
		{
			switch(c)
			{
			case 5:		/* answerback?? */
				printf("%c[?6c",27);
			break;
			case 7:		/* bell character, ignore */
			break;
			case 8:		/* backspace */
				SetCursorX(m_cursorx-1);
			break;
			case 9:		/* tab */
				/* todo: handle tab stops properly */
				do
				{
					PutAtCursor(' ');
				}while(m_cursorx&3);
			break;
			case 10:	/* line feed */
			case 11:
			case 12:
				CursorDown(1);
				if(m_autocr)
					goto docr;
			break;
			case 13:	/* carraige return */
docr:;				/* not sure if this is correct */
                m_style.ClearStyleBit(STYLE_DOUBLETOP);
                m_style.ClearStyleBit(STYLE_DOUBLEBOTTOM);
				/* clear wrap flag just incase */
				m_cursorwrap=false;
				SetCursorX(0);
			break;
			case 14:
				/* Invoke G1 character set. */
				m_curset=1;
			break;
			case 15:
				/* Invoke G0 character set. */
				m_curset=0;
			break;
			default:
			{
				unsigned int c16=c;
				/* just a regular character, print it at the cursor position */
				switch(m_charsets[m_curset])
				{
				case CHARSET_NORMAL:
					/* do nothing */
				break;
				case CHARSET_PC:
					switch(c16)
					{
					case 0xb3:
						/* vert line */
						c16='|';
					break;
					case 0xb4:
						/* right t */
						c16=0x2524;
					break;
					case 0xbf:
						/* top right corner */
						c16=0x2510;
					break;
					case 0xc0:
						/* bottom left corner */
						c16=0x2514;
					break;
					case 0xc3:
						/* left t */
						c16=0x251c;
					break;
					case 0xc4:
						/* horiz line */
						c16='-';
					break;
					case 0xd9:
						/* bottom right corner */
						c16=0x2518;
					break;
					case 0xda:
						/* top left corner */
						c16=0x250c;
					break;
					default:
						//dlog("PC char '%c' %02x\r\n",c,c);
					break;
					}
				break;
				case CHARSET_VT100GRAPHICS:
					switch(c16)
					{
					case 'a':
						c16='#';
					break;
					case 'f':
						c16='o';
					break;
					case 'j':	/* bottom right corner */
						c16=0x2518;
					break;
					case 'k':	/* top right corner */
						c16=0x2510;
					break;
					case 'l':	/* top left corner */
						c16=0x250c;
					break;
					case 'm':	/* bottom left corner */
						c16=0x2514;
					break;
					case 'n':	/* crossing lines */
						c16=0x253c;
					break;
				    case 'o':	/* horizontal line - scan 1 */
						c16=0x2500;
					break;
				    case 'p':	/* horizontal line - scan 2 */
						c16=0x0305;
					break;
				    case 'q':	/* horizontal line - scan 3 */
						c16='-';
					break;
				    case 'r':	/* horizontal line - scan 4 */
						c16=0x0336;
					break;
				    case 's':	/* horizontal line - scan 5 */
						c16='_';
					break;
				    case 't':	/* left t */
						c16=0x251c;
					break;
				    case 'u':	/* right t */
						c16=0x2524;
					break;
				    case 'v':	/* bottom t */
						c16=0x2534;
					break;
				    case 'w':	/* top t */
						c16=0x252c;
					break;
					case 'x':	/* vertical bar */
						c16=0x2502;	
					break;
					case ' ':
						/* leave */
					break;
					default:
						//dlog("Graphics char '%c' %02x\r\n",c,c);
					break;
					}
				break;
				}
				PutAtCursor(c16);
			}
			break;
			}
		}
	}
}

/* since we are using a rotating buffer we need to calculate the actual line */
int Tty::GetLineIndex(int y)
{
	int line;

	line=(m_topline+m_historylines+y)%m_numlines;
	return(line);
}

void Tty::CursorLeft(int n)
{
	int newx=m_cursorx-n;

	if(newx<0)
		newx=0;
	SetCursorX(newx);
}

void Tty::CursorRight(int n)
{
	int newx=m_cursorx+n;

	if(newx>=m_numcols)
	{
		newx=m_numcols-1;
		m_cursorwrap=true;
	}
	SetCursorX(newx);
}

void Tty::CursorDown(int n)
{
	/* cursory must stay within the visible area */
	/* history lines are the number that have scrolled off the top */
	/* top line is a rotating index that iterates and wraps to change the current top line */
	m_cursorwrap=false;
	m_changed=true;
	m_line[GetCursorLineIndex()]->SetTime(m_time+1);

	while(n)
	{
		--n;
		if((m_cursory==m_scrollbottom) && ((m_scrollbottom!=(m_numshowlines-1)) || (m_scrolltop!=0)))
		{
			/* scroll sub-area up */
			int y=m_scrolltop;
			while(y<m_scrollbottom)
			{
				SwapLines(GetLineIndex(y),GetLineIndex(y+1));
				++y;
			}
			GetLine(GetLineIndex(m_scrollbottom))->Clear(m_time+1);
		}
		else
		{
			if((m_cursory+1)<m_numshowlines)
				++m_cursory;
			else if((m_historylines)<(m_numlines-m_numshowlines))
				++m_historylines;
			else
			{
				if((m_topline+1)<m_numlines)
					++m_topline;
				else
					m_topline=0;

				//clear this line to blank!
				m_line[GetCursorLineIndex()]->Clear(m_time+1);
			}
		}
	}
	m_line[GetCursorLineIndex()]->SetTime(m_time+1);
	SetCursorX(m_cursorx);
	//dlog("cursor y=%d,hist=%d,topline=%d\n",m_cursory,m_historylines,m_topline);
}

void Tty::PutAtCursor(unsigned int c)
{
	int line=GetCursorLineIndex();

	if(m_cursorwrap)
	{
		SetCursorX(0);
		CursorDown(1);
	}
	m_changed=true;
	//dlog("Tty::PutAtCursor time=%d line=%d,col=%d,char=%d\n",m_time+1,line,m_cursorx,c);
	if(m_insertchars)
	{
		m_line[line]->InsertChar(m_cursorx);
		--m_insertchars;
	}
	m_line[line]->SetChar(m_time+1,m_cursorx,c,&m_style);
	CursorRight(1);

	/* if we are in double width/height mode then duplicate the character since it occupies 2 spots */
	if(m_style.GetStyle()&(STYLE_DOUBLETOP|STYLE_DOUBLEBOTTOM))
	{
		m_line[line]->SetChar(m_time+1,m_cursorx,c,&m_style);
		CursorRight(1);
	}
}

Tty::~Tty()
{
	delete []m_line;
}

#define READEND 0
#define WRITEEND 1

int startdaemon(int autostart,const char *defshell)
{
	int n;
	int cpipe[2];
	struct winsize ws;
	/* Our process ID and Session ID */
	pid_t pid, sid;
	int shmid;
	int ptyfd;
	SHAREDMEM_DEF *psm;
	key_t shmkey=USEKEY;

	dlog("start daemon!\n");
	 /* Change the file mode mask */
    umask(0);

    /* Fork off the parent process */
    pid = fork();
    if (pid < 0)
	{
		dlog("Fork error errno=%d!\n",errno);
		return(EXIT_FAILURE);
	}
	/* If we got a good PID, then
       we can exit the parent process. */
    if (pid > 0)
		return(EXIT_SUCCESS);

	//this is a hack since we need stdin/stdout to stay pointing at apache
	//and forkpty will take them over if we don't do this!
	dlog("creating temp pipes!\n");
	if(pipe(cpipe)<0)
	{
		dlog("Pipe error!\n");
		return(EXIT_FAILURE);
	}

	dup2(cpipe[READEND], STDIN_FILENO);
	dup2(cpipe[WRITEEND], STDOUT_FILENO);

    /* Create a new SID for the child process */
    sid = setsid();
    if (sid < 0)
	{
		dlog("SetSid error!\n");
		return(EXIT_FAILURE);
	}

    /* Create the shared memory segment. */
    if ((shmid = shmget(shmkey, sizeof(SHAREDMEM_DEF), IPC_EXCL|IPC_CREAT|0666)) < 0)
	{
		dlog("Shmget error creating shared memory errno=%d!\n",errno);
		return(EXIT_FAILURE);
    }

	psm=(SHAREDMEM_DEF *)shmat(shmid, NULL, 0);
	memset(psm,0,sizeof(SHAREDMEM_DEF));
	psm->ready=0xdeadbeef;
	dlog("shared memory is at %08x\n",psm);
	/* generate the two pipes for communicating to the shell */
	 //fork off the login prompt
	 ws.ws_row=NUMSHOWLINES;
	 ws.ws_col=NUMCOLS;
     pid = forkpty(&ptyfd, NULL, NULL, &ws);
     if (pid < 0)
	 {
		 dlog("Forkpty error! errno=%d\n",errno);
		shmdt(psm);
		shmctl(shmid, IPC_RMID, 0);
		 return(EXIT_FAILURE);
	 }
	 //child process gets 0, parent gets non zero!
	 if(pid==0)
	 {
	    setenv("TERM", "linux", 1);
		dlog("starting shell '%s'\n",defshell);
		if(execl(defshell, defshell, "-i",NULL, NULL )<0)
		{
			printf("Could not run %s\n",defshell);
			dlog("could not run shell '%s'\n",defshell);

			if(execl("/bin/sh", "/bin/sh", "-i",NULL, NULL )<0)
		 	{
		 		printf("Could not run /bin/sh\n");
				dlog("could not run shell /bin/sh\n");

		 		/* could not run /bin/sh, lets try /bin/bash */
		 		if(execl("/bin/bash", "/bin/bash", "-i",NULL, NULL )<0)
		 		{
					 /* nope could not run it either */
					 printf("Could not run /bin/bash\n");
					 dlog("could not run shell /bin/bash\n");
				 }
			}
		 }
		dlog("child exit!\n");
		/* should never get here */
		 return(EXIT_FAILURE);
	}

	dlog("starting loop\n");
    /* The Big Loop, get input from port and print to login */
	{
		char fromshell[32768];	//buffered characters recieved from the login/shell
		int i;
		unsigned char c;
		int dopause;
		fd_set rfds;
   		struct timeval tv;
		Tty *tty;

		//allocate tty object to hold the tty output
		tty=new Tty(NUMLINES,NUMSHOWLINES,NUMCOLS);
#if DOTTYTEST
		{
			static const char ttytest[]={"\
\x1b[0m\
\x1b#3Double Wide Double tall! (normal)\r\n\
\x1b#4Double Wide Double tall! (normal)\r\n\
\x1b[0m\
\x1b[1m\
\x1b#3Double Wide Double tall! (bold)\r\n\
\x1b#4Double Wide Double tall! (bold)\r\n\
\x1b[0m\
\x1b[4m\
\x1b#3Double Wide Double tall! (underlined)\r\n\
\x1b#4Double Wide Double tall! (underlined)\r\n\
\x1b[0m\
\x1b[5m\
\x1b#3Double Wide Double tall! (blink)\r\n\
\x1b#4Double Wide Double tall! (blink)\r\n\
\x1b[0m\
\x1b[7m\
\x1b#3Double Wide Double tall! (reverse)\r\n\
\x1b#4Double Wide Double tall! (reverse)\r\n\
\x1b[0m\
\x1b[40m\x1b[30mX\x1b[31mX\x1b[32mX\x1b[33mX\x1b[34mX\x1b[35mX\x1b[36mX\x1b[37mX\
\x1b[41m\x1b[30mX\x1b[31mX\x1b[32mX\x1b[33mX\x1b[34mX\x1b[35mX\x1b[36mX\x1b[37mX\
\x1b[42m\x1b[30mX\x1b[31mX\x1b[32mX\x1b[33mX\x1b[34mX\x1b[35mX\x1b[36mX\x1b[37mX\
\x1b[43m\x1b[30mX\x1b[31mX\x1b[32mX\x1b[33mX\x1b[34mX\x1b[35mX\x1b[36mX\x1b[37mX\
\x1b[44m\x1b[30mX\x1b[31mX\x1b[32mX\x1b[33mX\x1b[34mX\x1b[35mX\x1b[36mX\x1b[37mX\
\x1b[45m\x1b[30mX\x1b[31mX\x1b[32mX\x1b[33mX\x1b[34mX\x1b[35mX\x1b[36mX\x1b[37mX\
\x1b[46m\x1b[30mX\x1b[31mX\x1b[32mX\x1b[33mX\x1b[34mX\x1b[35mX\x1b[36mX\x1b[37mX\
\x1b[47m\x1b[30mX\x1b[31mX\x1b[32mX\x1b[33mX\x1b[34mX\x1b[35mX\x1b[36mX\x1b[37mX\x1b[30m (normal)\r\n\
\x1b[0m\
\x1b[1m\
\x1b[40m\x1b[30mX\x1b[31mX\x1b[32mX\x1b[33mX\x1b[34mX\x1b[35mX\x1b[36mX\x1b[37mX\
\x1b[41m\x1b[30mX\x1b[31mX\x1b[32mX\x1b[33mX\x1b[34mX\x1b[35mX\x1b[36mX\x1b[37mX\
\x1b[42m\x1b[30mX\x1b[31mX\x1b[32mX\x1b[33mX\x1b[34mX\x1b[35mX\x1b[36mX\x1b[37mX\
\x1b[43m\x1b[30mX\x1b[31mX\x1b[32mX\x1b[33mX\x1b[34mX\x1b[35mX\x1b[36mX\x1b[37mX\
\x1b[44m\x1b[30mX\x1b[31mX\x1b[32mX\x1b[33mX\x1b[34mX\x1b[35mX\x1b[36mX\x1b[37mX\
\x1b[45m\x1b[30mX\x1b[31mX\x1b[32mX\x1b[33mX\x1b[34mX\x1b[35mX\x1b[36mX\x1b[37mX\
\x1b[46m\x1b[30mX\x1b[31mX\x1b[32mX\x1b[33mX\x1b[34mX\x1b[35mX\x1b[36mX\x1b[37mX\
\x1b[47m\x1b[30mX\x1b[31mX\x1b[32mX\x1b[33mX\x1b[34mX\x1b[35mX\x1b[36mX\x1b[37mX\x1b[30m (bold)\r\n\
\x1b[0m\
\x1b[4m\
\x1b[40m\x1b[30mX\x1b[31mX\x1b[32mX\x1b[33mX\x1b[34mX\x1b[35mX\x1b[36mX\x1b[37mX\
\x1b[41m\x1b[30mX\x1b[31mX\x1b[32mX\x1b[33mX\x1b[34mX\x1b[35mX\x1b[36mX\x1b[37mX\
\x1b[42m\x1b[30mX\x1b[31mX\x1b[32mX\x1b[33mX\x1b[34mX\x1b[35mX\x1b[36mX\x1b[37mX\
\x1b[43m\x1b[30mX\x1b[31mX\x1b[32mX\x1b[33mX\x1b[34mX\x1b[35mX\x1b[36mX\x1b[37mX\
\x1b[44m\x1b[30mX\x1b[31mX\x1b[32mX\x1b[33mX\x1b[34mX\x1b[35mX\x1b[36mX\x1b[37mX\
\x1b[45m\x1b[30mX\x1b[31mX\x1b[32mX\x1b[33mX\x1b[34mX\x1b[35mX\x1b[36mX\x1b[37mX\
\x1b[46m\x1b[30mX\x1b[31mX\x1b[32mX\x1b[33mX\x1b[34mX\x1b[35mX\x1b[36mX\x1b[37mX\
\x1b[47m\x1b[30mX\x1b[31mX\x1b[32mX\x1b[33mX\x1b[34mX\x1b[35mX\x1b[36mX\x1b[37mX\x1b[30m (underlined)\r\n\
\x1b[0m\
\x1b[5m\
\x1b[40m\x1b[30mX\x1b[31mX\x1b[32mX\x1b[33mX\x1b[34mX\x1b[35mX\x1b[36mX\x1b[37mX\
\x1b[41m\x1b[30mX\x1b[31mX\x1b[32mX\x1b[33mX\x1b[34mX\x1b[35mX\x1b[36mX\x1b[37mX\
\x1b[42m\x1b[30mX\x1b[31mX\x1b[32mX\x1b[33mX\x1b[34mX\x1b[35mX\x1b[36mX\x1b[37mX\
\x1b[43m\x1b[30mX\x1b[31mX\x1b[32mX\x1b[33mX\x1b[34mX\x1b[35mX\x1b[36mX\x1b[37mX\
\x1b[44m\x1b[30mX\x1b[31mX\x1b[32mX\x1b[33mX\x1b[34mX\x1b[35mX\x1b[36mX\x1b[37mX\
\x1b[45m\x1b[30mX\x1b[31mX\x1b[32mX\x1b[33mX\x1b[34mX\x1b[35mX\x1b[36mX\x1b[37mX\
\x1b[46m\x1b[30mX\x1b[31mX\x1b[32mX\x1b[33mX\x1b[34mX\x1b[35mX\x1b[36mX\x1b[37mX\
\x1b[47m\x1b[30mX\x1b[31mX\x1b[32mX\x1b[33mX\x1b[34mX\x1b[35mX\x1b[36mX\x1b[37mX\x1b[30m (blink)\r\n\
\x1b[0m\
\x1b[7m\
\x1b[40m\x1b[30mX\x1b[31mX\x1b[32mX\x1b[33mX\x1b[34mX\x1b[35mX\x1b[36mX\x1b[37mX\
\x1b[41m\x1b[30mX\x1b[31mX\x1b[32mX\x1b[33mX\x1b[34mX\x1b[35mX\x1b[36mX\x1b[37mX\
\x1b[42m\x1b[30mX\x1b[31mX\x1b[32mX\x1b[33mX\x1b[34mX\x1b[35mX\x1b[36mX\x1b[37mX\
\x1b[43m\x1b[30mX\x1b[31mX\x1b[32mX\x1b[33mX\x1b[34mX\x1b[35mX\x1b[36mX\x1b[37mX\
\x1b[44m\x1b[30mX\x1b[31mX\x1b[32mX\x1b[33mX\x1b[34mX\x1b[35mX\x1b[36mX\x1b[37mX\
\x1b[45m\x1b[30mX\x1b[31mX\x1b[32mX\x1b[33mX\x1b[34mX\x1b[35mX\x1b[36mX\x1b[37mX\
\x1b[46m\x1b[30mX\x1b[31mX\x1b[32mX\x1b[33mX\x1b[34mX\x1b[35mX\x1b[36mX\x1b[37mX\
\x1b[47m\x1b[30mX\x1b[31mX\x1b[32mX\x1b[33mX\x1b[34mX\x1b[35mX\x1b[36mX\x1b[37mX\x1b[30m (reverse)\r\n\
\x1b[0m"};
			for(i=0;i<(int)(sizeof(ttytest));++i)
				tty->Print(ttytest[i]);
		}
#endif
		while(1)
		{
			/* does the cgi-bin want us to shutdown? */
			if(psm->killdaemon && autostart)
				break;

			/* get input from cgi-bin and send to the shell */
			dopause=0;
			while(SH_IsEmpty(&psm->cgitod)==false)
			{
				c=SH_Get(&psm->cgitod);
				//nulls get sent as a heartbeat so don't send them to the shell
				if(c)
				{
					if(write(ptyfd,&c,1)!=1)
						dlog("Error writing to shell!\n");
					dopause=1;
					//dlog("'%02x ",c);
				}
			}

			if(dopause)
			{
				//dlog("\ngot data from cgi-bin so we are sleeping\n");
				sleepms(100);	/* allow some time for the shell to generate output */
			}
			/* get output from the shell */
			while(1)
			{
		        FD_ZERO(&rfds);
		        FD_SET(ptyfd, &rfds);
		        tv.tv_sec = 0;
		        tv.tv_usec = 1*1000;
		        n = select(ptyfd + 1, &rfds, NULL, NULL, &tv);
				if(!n)
					break;
				/* read from shell (child process) */
				n=read(ptyfd,fromshell,sizeof(fromshell));
				if(n<=0)
					break;
				/* send chars to tty handler */
				for(i=0;i<n;++i)
					tty->Print(fromshell[i]);
			}

			/* is the cgi-bin waiting for data? */
			if(psm->cgiready)
			{
				int nl=tty->GetNumLines();
				int nc=tty->GetNumColumns();
				int topline=tty->GetTopLine();
				unsigned int lineschanged;
				unsigned int slotschanged;
				unsigned int ajaxtime;
				unsigned int lastreceived=psm->cgiready;
				int slot;
				int cl=tty->GetCursorLineIndex();
				unsigned int c16;

				//check to see if cursor moved since last update
				tty->CheckCursorMoved();

				/* increment 'time' if any changes since last update */
				if(tty->GetChanged())
				{
					tty->SetChanged(false);
					tty->SetTime(tty->GetTime()+1);
					//dlog("tty changed new ajaxtime=%d\n",tty->GetTime());
				}
				ajaxtime=tty->GetTime();

				//if the daemon shutdown and was re-started then the javascript can have a time that is 
				//no longer valid so re-send everything in this case
				if(lastreceived>ajaxtime)
					lastreceived=0;

				//dlog("lastrec=%d, ajaxtime=%d\n",lastreceived,ajaxtime);
				if(lastreceived<ajaxtime)
				{
					SHBITS_DEF bb;
					bool reverse=tty->GetReverse();

					SH_PutBitsInit(&bb);

					/* send current ajaxtime */
					SH_PutBits(&psm->dtocgi,&bb,ajaxtime,MAXTIMEBITS);

					/* top line */
					SH_PutBits(&psm->dtocgi,&bb,topline,NUMLINEBITS);

					/* count number of lines and slots changed */
					lineschanged=0;
					slotschanged=0;
					for(i=0;i<nl;++i)
					{
						TtyLine *ttyl=tty->GetLine(i);
						if(ttyl->GetTime()>lastreceived)
							++lineschanged;
						if(ttyl->GetSlotTime()>lastreceived)
							++slotschanged;
					}

					/* these are only changed when inserting or deleting a line */
					if(slotschanged==0)
						SH_PutBits(&psm->dtocgi,&bb,0,1);
					else
					{
						SH_PutBits(&psm->dtocgi,&bb,1,1);
						SH_PutBits(&psm->dtocgi,&bb,slotschanged,NUMLINEBITS);
						for(i=0;i<nl;++i)
						{
							TtyLine *ttyl=tty->GetLine(i);
							if(ttyl->GetSlotTime()>lastreceived)
							{
								slot=ttyl->GetSlot();

								SH_PutBits(&psm->dtocgi,&bb,i,NUMLINEBITS);
								SH_PutBits(&psm->dtocgi,&bb,slot,NUMLINEBITS);
								//dlog("sending slot change index#%d = slot #%d\n",i,slot);
							}
						}
					}

					/* send number of lines changed since this time */
					SH_PutBits(&psm->dtocgi,&bb,lineschanged,NUMLINEBITS);

					for(i=0;i<nl;++i)
					{
						TtyLine *ttyl=tty->GetLine(i);

						if(ttyl->GetTime()>lastreceived)
						{
							unsigned int lstylebits=0;
							//send line slot number
							slot=ttyl->GetSlot();
							SH_PutBits(&psm->dtocgi,&bb,slot,NUMLINEBITS);
							//dlog("sending line at slot changed slot #%d\n",slot);

							for(int j=0;j<nc;++j)
							{
								TtyChar *ttyc=ttyl->GetChar(j);
								unsigned int stylebits=ttyc->GetStyleBits();

								c16=ttyc->GetCharCode();
								if(!c16)
								{
									if(reverse==false)
										break;
									/* for reverse mode only we pad the line with spaces */
									c16=' ';
								}

								if(c16<128)
								{
									SH_PutBits(&psm->dtocgi,&bb,0,1);		//0 bit
									SH_PutBits(&psm->dtocgi,&bb,c16&127,7);	//7 bits
								}
								else
								{
									SH_PutBits(&psm->dtocgi,&bb,1,1);		//1 bit
									SH_PutBits(&psm->dtocgi,&bb,c16,16);		//16 bits
								}
								/* send along the style for this character */
								if((tty->GetShowCursor()==true) && (i==cl) && (j==tty->GetCursorX()))
								{
									//dlog("setting cursor on this line!\n");
									stylebits|=STYLE_CURSOR;
								}
								if(reverse)
									stylebits^=STYLE_REVERSE;

								if(stylebits==lstylebits && j)
								{
									//repeat last style flag bit=1
									SH_PutBits(&psm->dtocgi,&bb,1,1);
								}
								else
								{
									//new style flag bit == 0
									SH_PutBits(&psm->dtocgi,&bb,0,1);
									SH_PutBits(&psm->dtocgi,&bb,stylebits,STYLE_BITSUSED+STYLE_COLORBITS+STYLE_COLORBITS);
									lstylebits=stylebits;
								}
								/* if we are in double width mode then skip the next character */
								if(stylebits&(STYLE_DOUBLETOP|STYLE_DOUBLEBOTTOM))
									++j;
							}
							SH_PutBits(&psm->dtocgi,&bb,0,8);
						}
					}
					SH_PutBitsFlush(&psm->dtocgi,&bb);
				}
				/* tell cgi-bin we have finished sending data */
				psm->cgiready=0;
			}
		}
		delete tty;
	}
	shmdt(psm);
	shmctl(shmid, IPC_RMID, 0);
	/* kill child process (shell) */
	if(pid)
		kill(pid,SIGKILL);
	dlog("daemon shutdown!\n");
	return(EXIT_SUCCESS);
}

int main(int argc,char **argv)
{
	int rc;
	uid_t euid=geteuid();
	uid_t egid=getegid();

	//elevate to root!
	rc=setuid(euid);
	if(rc<0)
		dlog("SetUID failed!\n");
	rc=setgid(egid);
	if(rc<0)
		dlog("SetGID failed!\n");

	//optionally we can have the daemon startup in your rc.local and in this case it will
	//stay running and not start/stop each time the user acesses the shell panel
	if(argc==2 && !strcmp(argv[1],"-d"))
		return(startdaemon(0,"/bin/bash"));

	mycgi();
}
